Path: blob/main/src/gui/GUITLLogicPhasesTrackerWindow.cpp
169665 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GUITLLogicPhasesTrackerWindow.cpp14/// @author Daniel Krajzewicz15/// @author Jakob Erdmann16/// @author Michael Behrisch17/// @date Oct/Nov 200318///19// A window displaying the phase diagram of a tl-logic20/****************************************************************************/21#include <config.h>2223#include <vector>24#include <iostream>25#include <utils/gui/windows/GUIMainWindow.h>26#include <utils/gui/div/GLHelper.h>27#include "GUITLLogicPhasesTrackerWindow.h"28#include <microsim/traffic_lights/MSTrafficLightLogic.h>29#include <microsim/output/MSInductLoop.h>30#include <microsim/MSLink.h>31#include <utils/common/ToString.h>32#include <utils/common/MsgHandler.h>33#include <guisim/GUITrafficLightLogicWrapper.h>34#include <utils/gui/windows/GUIAppEnum.h>35#include <utils/gui/images/GUIIconSubSys.h>36#include <utils/gui/settings/GUIVisualizationSettings.h>37#include <utils/gui/div/GUIDesigns.h>38#include <foreign/fontstash/fontstash.h>39#include <utils/gui/globjects/GLIncludes.h>404142// ===========================================================================43// static member initialisation44// ===========================================================================45int GUITLLogicPhasesTrackerWindow::myLastY(-1);4647// ===========================================================================48// member method definitions49// ===========================================================================50/* -------------------------------------------------------------------------51* GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-callbacks52* ----------------------------------------------------------------------- */53FXDEFMAP(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel) GUITLLogicPhasesTrackerPanelMap[] = {54FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onConfigure),55FXMAPFUNC(SEL_PAINT, 0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onPaint),56FXMAPFUNC(SEL_MOTION, 0, GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onMouseMove),5758};5960// Macro for the GLTestApp class hierarchy implementation61FXIMPLEMENT(GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel, FXGLCanvas, GUITLLogicPhasesTrackerPanelMap, ARRAYNUMBER(GUITLLogicPhasesTrackerPanelMap))62636465/* -------------------------------------------------------------------------66* GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel-methods67* ----------------------------------------------------------------------- */68GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::GUITLLogicPhasesTrackerPanel(69FXComposite* c, GUIMainWindow& app,70GUITLLogicPhasesTrackerWindow& parent) :71FXGLCanvas(c, app.getGLVisual(), app.getBuildGLCanvas(), (FXObject*) nullptr, (FXSelector) 0, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y/*, 0, 0, 300, 200*/),72myParent(&parent)73{}747576GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::~GUITLLogicPhasesTrackerPanel() {}777879long80GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onConfigure(FXObject*, FXSelector, void*) {81if (makeCurrent()) {82int widthInPixels = getWidth();83int heightInPixels = getHeight();84if (widthInPixels != 0 && heightInPixels != 0) {85glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);86glClearColor(0, 0, 0, 1);87glDisable(GL_DEPTH_TEST);88glDisable(GL_LIGHTING);89glDisable(GL_LINE_SMOOTH);90glEnable(GL_BLEND);91glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);92glEnable(GL_ALPHA_TEST);93glDisable(GL_COLOR_MATERIAL);94glLineWidth(1);95glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);96}97}98return 1;99}100101102long103GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onPaint(104FXObject*, FXSelector, void*) {105if (!isEnabled()) {106return 1;107}108if (makeCurrent()) {109int widthInPixels = getWidth();110int heightInPixels = getHeight();111if (widthInPixels != 0 && heightInPixels != 0) {112glViewport(0, 0, widthInPixels - 1, heightInPixels - 1);113glClearColor(0, 0, 0, 1);114glDisable(GL_DEPTH_TEST);115glDisable(GL_LIGHTING);116glDisable(GL_LINE_SMOOTH);117glEnable(GL_BLEND);118glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);119glEnable(GL_ALPHA_TEST);120glDisable(GL_COLOR_MATERIAL);121glLineWidth(1);122glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);123// draw124glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);125myParent->drawValues(*this);126swapBuffers();127}128makeNonCurrent();129}130return 1;131}132133134long135GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerPanel::onMouseMove(FXObject*, FXSelector, void* ptr) {136FXEvent* event = (FXEvent*) ptr;137myMousePos.setx(event->win_x);138myMousePos.sety(event->win_y);139onPaint(nullptr, 0, nullptr);140return 1;141}142143/* -------------------------------------------------------------------------144* GUITLLogicPhasesTrackerWindow - FOX callback mapping145* ----------------------------------------------------------------------- */146FXDEFMAP(GUITLLogicPhasesTrackerWindow) GUITLLogicPhasesTrackerWindowMap[] = {147FXMAPFUNC(SEL_CONFIGURE, 0, GUITLLogicPhasesTrackerWindow::onConfigure),148FXMAPFUNC(SEL_PAINT, 0, GUITLLogicPhasesTrackerWindow::onPaint),149FXMAPFUNC(SEL_COMMAND, MID_SIMSTEP, GUITLLogicPhasesTrackerWindow::onSimStep),150151};152153FXIMPLEMENT(GUITLLogicPhasesTrackerWindow, FXMainWindow, GUITLLogicPhasesTrackerWindowMap, ARRAYNUMBER(GUITLLogicPhasesTrackerWindowMap))154155156/* -------------------------------------------------------------------------157* GUITLLogicPhasesTrackerWindow-methods158* ----------------------------------------------------------------------- */159GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerWindow(160GUIMainWindow& app,161MSTrafficLightLogic& logic, GUITrafficLightLogicWrapper& wrapper,162ValueSource<std::pair<SUMOTime, MSPhaseDefinition> >* src) :163FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL, 20, 20, 300, 200),164myApplication(&app),165myTLLogic(&logic),166myAmInTrackingMode(true) {167initToolBar();168myConnector = new GLObjectValuePassConnector<std::pair<SUMOTime, MSPhaseDefinition> >(wrapper, src, this);169app.addChild(this);170for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {171myLinkNames.push_back(toString<int>(i));172}173for (auto item : myTLLogic->getDetectorStates()) {174std::string detID = item.first;175if (detID.size() > 4) {176detID = detID.substr(detID.size() - 4);177}178myDetectorNames.push_back(detID);179}180for (auto item : myTLLogic->getConditions()) {181myConditionNames.push_back(item.first);182}183loadSettings();184const int newHeight = computeHeight();185FXScrollWindow* scrollWindow = new FXScrollWindow(this, LAYOUT_FILL_X | LAYOUT_FILL_Y | HSCROLLER_NEVER | FRAME_NONE);186FXHorizontalFrame* spacerFrame = new FXHorizontalFrame(scrollWindow, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y | FRAME_NONE);187new FXScrollWindow(spacerFrame, LAYOUT_FIX_WIDTH | LAYOUT_FIX_HEIGHT | FRAME_NONE, 0, 0, 0, newHeight - 40);188FXVerticalFrame* glcanvasFrame =189new FXVerticalFrame(spacerFrame,190FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,1910, 0, 0, 0, 0, 0, 0, 0);192myPanel = new GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);193setTitle((logic.getID() + " - " + logic.getProgramID() + " - tracker").c_str());194setIcon(GUIIconSubSys::getIcon(GUIIcon::APP_TLSTRACKER));195setHeight(newHeight);196}197198199GUITLLogicPhasesTrackerWindow::GUITLLogicPhasesTrackerWindow(200GUIMainWindow& app,201MSTrafficLightLogic& logic, GUITrafficLightLogicWrapper& /*wrapper*/,202const MSSimpleTrafficLightLogic::Phases& /*phases*/) :203FXMainWindow(app.getApp(), "TLS-Tracker", nullptr, nullptr, DECOR_ALL, 20, 20, 300, 200),204myApplication(&app),205myTLLogic(&logic),206myAmInTrackingMode(false),207myToolBarDrag(nullptr),208myBeginOffset(nullptr) {209myConnector = nullptr;210initToolBar();211app.addChild(this);212for (int i = 0; i < (int)myTLLogic->getLinks().size(); ++i) {213myLinkNames.push_back(toString<int>(i));214}215int newHeight = computeHeight();216FXScrollWindow* scrollWindow = new FXScrollWindow(this, LAYOUT_FILL_X | LAYOUT_FILL_Y | HSCROLLER_NEVER | FRAME_NONE);217FXHorizontalFrame* spacerFrame = new FXHorizontalFrame(scrollWindow, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y | FRAME_NONE);218new FXScrollWindow(spacerFrame, LAYOUT_FIX_WIDTH | LAYOUT_FIX_HEIGHT | FRAME_NONE, 0, 0, 0, newHeight - 40);219FXVerticalFrame* glcanvasFrame =220new FXVerticalFrame(spacerFrame,221FRAME_SUNKEN | LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y,2220, 0, 0, 0, 0, 0, 0, 0);223myPanel = new GUITLLogicPhasesTrackerPanel(glcanvasFrame, *myApplication, *this);224setTitle((logic.getID() + " - " + logic.getProgramID() + " - phases").c_str());225setIcon(GUIIconSubSys::getIcon(GUIIcon::APP_TLSTRACKER));226setHeight(newHeight);227setWidth(700);228}229230231GUITLLogicPhasesTrackerWindow::~GUITLLogicPhasesTrackerWindow() {232if (myAmInTrackingMode) {233saveSettings();234myLastY = -1;235}236myApplication->removeChild(this);237delete myConnector;238// just to quit cleanly on a failure239if (myLock.locked()) {240myLock.unlock();241}242delete myToolBarDrag;243}244245void246GUITLLogicPhasesTrackerWindow::initToolBar() {247myToolBarDrag = new FXToolBarShell(this, GUIDesignToolBar);248myToolBar = new FXToolBar(this, myToolBarDrag, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | FRAME_RAISED);249new FXToolBarGrip(myToolBar, myToolBar, FXToolBar::ID_TOOLBARGRIP, GUIDesignToolBarGrip);250251if (myAmInTrackingMode) {252// interval manipulation253new FXLabel(myToolBar, "range (s):", nullptr, LAYOUT_CENTER_Y);254myBeginOffset = new FXRealSpinner(myToolBar, 4, this, MID_SIMSTEP, LAYOUT_TOP | FRAME_SUNKEN | FRAME_THICK);255//myBeginOffset->setFormatString("%.0f");256//myBeginOffset->setIncrements(1, 10, 100);257myBeginOffset->setIncrement(10);258myBeginOffset->setRange(60, 3600);259myBeginOffset->setValue(240);260}261262new FXLabel(myToolBar, "time style:", nullptr, LAYOUT_CENTER_Y);263myTimeMode = new MFXComboBoxIcon(myToolBar, nullptr, false, GUIDesignComboBoxVisibleItems,264this, MID_SIMSTEP, GUIDesignViewSettingsComboBox1);265myTimeMode->appendIconItem("seconds");266myTimeMode->appendIconItem("MM:SS");267myTimeMode->appendIconItem("time in cycle");268269new FXLabel(myToolBar, "green time", nullptr, LAYOUT_CENTER_Y);270myGreenMode = new MFXComboBoxIcon(myToolBar, nullptr, false, GUIDesignComboBoxVisibleItems,271this, MID_SIMSTEP, GUIDesignViewSettingsComboBox1);272myGreenMode->appendIconItem("off");273myGreenMode->appendIconItem("phase");274myGreenMode->appendIconItem("running");275276myIndexMode = new FXCheckButton(myToolBar, TL("phase names"), this, MID_SIMSTEP);277278if (myAmInTrackingMode) {279myDetectorMode = new FXCheckButton(myToolBar, TL("detectors"), this, MID_SIMSTEP);280myConditionMode = new FXCheckButton(myToolBar, TL("conditions"), this, MID_SIMSTEP);281} else {282myDetectorMode = nullptr;283myConditionMode = nullptr;284}285}286287288void289GUITLLogicPhasesTrackerWindow::create() {290FXMainWindow::create();291if (myToolBarDrag != nullptr) {292myToolBarDrag->create();293}294}295296int297GUITLLogicPhasesTrackerWindow::computeHeight() {298int newHeight = (int)myTLLogic->getLinks().size() * 20 + 30 + 8 + 30 + 60;299if (myAmInTrackingMode) {300newHeight += 20; // time bar301newHeight += 10; // something extra caused by the scroll frames302if (myDetectorMode->getCheck()) {303newHeight += (int)myTLLogic->getDetectorStates().size() * 20 + 5;304}305if (myConditionMode->getCheck()) {306newHeight += (int)myTLLogic->getConditions().size() * 20 + 5;307}308}309return newHeight;310}311312void313GUITLLogicPhasesTrackerWindow::drawValues(GUITLLogicPhasesTrackerPanel& caller) {314// compute what shall be shown (what is visible)315myFirstPhase2Show = 0;316myFirstPhaseOffset = 0;317SUMOTime leftOffset = 0;318myFirstDet2Show = 0;319myFirstDetOffset = 0;320myFirstCond2Show = 0;321myFirstCondOffset = 0;322myFirstTime2Show = 0;323if (!myAmInTrackingMode) {324myPhases.clear();325myDurations.clear();326myTimeInCycle.clear();327myPhaseIndex.clear();328// insert phases329MSSimpleTrafficLightLogic* simpleTLLogic = dynamic_cast<MSSimpleTrafficLightLogic*>(myTLLogic);330if (simpleTLLogic == nullptr) {331return;332}333myLastTime = 0;334myBeginTime = 0;335int idx = 0;336for (MSPhaseDefinition* const phase : simpleTLLogic->getPhases()) {337myPhases.push_back(*phase);338myDurations.push_back(phase->duration);339myTimeInCycle.push_back(myLastTime);340myPhaseIndex.push_back(idx++);341myLastTime += phase->duration;342}343if (myLastTime <= myBeginTime) {344WRITE_ERROR(TL("Overflow in time computation occurred."));345return;346}347} else {348SUMOTime beginOffset = TIME2STEPS(myBeginOffset->getValue());349myBeginTime = myLastTime - beginOffset;350myFirstTime2Show = myBeginTime;351// check whether no phases are known at all352if (myDurations.size() != 0) {353SUMOTime durs = 0;354int phaseOffset = (int)myDurations.size() - 1;355DurationsVector::reverse_iterator i = myDurations.rbegin();356while (i != myDurations.rend()) {357if (durs + (*i) > beginOffset) {358myFirstPhase2Show = phaseOffset;359myFirstPhaseOffset = (durs + (*i)) - beginOffset;360break;361}362durs += (*i);363phaseOffset--;364++i;365}366if (i == myDurations.rend()) {367// there are too few information stored;368myFirstPhase2Show = 0;369myFirstPhaseOffset = 0;370leftOffset = beginOffset - durs;371}372}373if (myDetectorDurations.size() != 0) {374SUMOTime durs = 0;375int phaseOffset = (int)myDetectorDurations.size() - 1;376DurationsVector::reverse_iterator i = myDetectorDurations.rbegin();377while (i != myDetectorDurations.rend()) {378if (durs + (*i) > beginOffset) {379myFirstDet2Show = phaseOffset;380myFirstDetOffset = (durs + (*i)) - beginOffset;381break;382}383durs += (*i);384phaseOffset--;385++i;386}387if (i == myDetectorDurations.rend()) {388// there are too few information stored;389myFirstDet2Show = 0;390myFirstDetOffset = 0;391}392}393if (myConditionDurations.size() != 0) {394SUMOTime durs = 0;395int phaseOffset = (int)myConditionDurations.size() - 1;396DurationsVector::reverse_iterator i = myConditionDurations.rbegin();397while (i != myConditionDurations.rend()) {398if (durs + (*i) > beginOffset) {399myFirstCond2Show = phaseOffset;400myFirstCondOffset = (durs + (*i)) - beginOffset;401break;402}403durs += (*i);404phaseOffset--;405++i;406}407if (i == myConditionDurations.rend()) {408// there are too few information stored;409myFirstCond2Show = 0;410myFirstCondOffset = 0;411}412}413}414// begin drawing415glMatrixMode(GL_PROJECTION);416glLoadIdentity();417glMatrixMode(GL_MODELVIEW);418glLoadIdentity();419glTranslated(-1, -1, 0);420glScaled(2, 2, 1);421glDisable(GL_TEXTURE_2D);422// draw the horizontal lines dividing the signal groups423glColor3d(1, 1, 1);424// compute some values needed more than once425const double panelHeight = (double) caller.getHeight();426const double panelWidth = (double) caller.getWidth();427const double barWidth = MAX2(1.0, panelWidth - 31);428const double fontHeight = 0.06 * 300. / panelHeight;429const double fontWidth = 0.06 * 300. / panelWidth;430const double h9 = 9. / panelHeight;431const double hTop = 20. / panelHeight;432const double h11 = 11. / panelHeight;433const double stateHeight = 16. / panelHeight;434const double h20 = 20. / panelHeight;435const double h30 = 15. / panelHeight;436const double h35 = 34. / panelHeight;437const double h60 = 70. / panelHeight;438const double h75 = 73. / panelHeight;439const double h80 = 90. / panelHeight;440const double w30 = 30 / panelWidth;441double h = 1. - hTop;442// draw the line below indices443glColor3d(1, 1, 1);444glBegin(GL_LINES);445glVertex2d(0, h);446glVertex2d(1, h);447glEnd();448// draw the link names and the lines dividing them449drawNames(myLinkNames, fontHeight, fontWidth, h20, w30, h, 0);450glBegin(GL_LINES);451glVertex2d(0, h + h20);452glVertex2d(1.0, h + h20);453glEnd();454455// draw the names closure (vertical line)456h += h20;457glColor3d(1, 1, 1);458glBegin(GL_LINES);459glVertex2d(w30, 1.);460glVertex2d(w30, h);461glEnd();462463if (myAmInTrackingMode) {464// optionally draw detector names465h -= h60;466if (myDetectorMode->getCheck()) {467const double top = h;468glBegin(GL_LINES);469glVertex2d(0, h);470glVertex2d(1.0, h);471glEnd();472drawNames(myDetectorNames, fontHeight * 0.7, fontWidth * 0.7, h20, w30, h, 3);473glBegin(GL_LINES);474glVertex2d(0, h + h20);475glVertex2d(1.0, h + h20);476glEnd();477// draw the names closure (vertical line)478glColor3d(1, 1, 1);479glBegin(GL_LINES);480glVertex2d(30. / panelWidth, top);481glVertex2d(30. / panelWidth, h + h20);482glEnd();483h -= h30;484}485// optionally draw condition names486if (myConditionMode->getCheck()) {487const double top = h;488glBegin(GL_LINES);489glVertex2d(0, h);490glVertex2d(1.0, h);491glEnd();492drawNames(myConditionNames, fontHeight * 0.7, fontWidth * 0.7, h20, w30, h, 3);493glBegin(GL_LINES);494glVertex2d(0, h + h20);495glVertex2d(1.0, h + h20);496glEnd();497// draw the names closure (vertical line)498glColor3d(1, 1, 1);499glBegin(GL_LINES);500glVertex2d(30. / panelWidth, top);501glVertex2d(30. / panelWidth, h + h20);502glEnd();503}504}505506// draw the phases507// disable value addition while drawing508myLock.lock();509// determine the initial offset510double x = 31. / panelWidth;511double ta = (double) leftOffset / panelWidth;512ta *= barWidth / ((double)(myLastTime - myBeginTime));513x += ta;514515// and the initial phase information516PhasesVector::iterator pi = myPhases.begin() + myFirstPhase2Show;517IndexVector::iterator ii = myPhaseIndex.begin() + myFirstPhase2Show;518519SUMOTime fpo = myFirstPhaseOffset;520const bool phaseNames = myIndexMode->getCheck() == TRUE;521std::string lastName = "";522double spaceForName = 0;523524// start drawing525std::vector<SUMOTime> runningGreen(myTLLogic->getLinks().size(), 0);526for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {527// the first phase may be drawn incompletely528SUMOTime duration = *pd - fpo;529// compute the height and the width of the phase530h = 1. - hTop;531double a = (double) duration / panelWidth;532a *= barWidth / ((double)(myLastTime - myBeginTime));533const double x2 = x + a;534535// go through the links536for (int j = 0; j < (int) myTLLogic->getLinks().size(); ++j) {537// determine the current link's color538LinkState state = pi->getSignalState(j);539// draw the bar (red is drawn as a line)540GLHelper::setColor(GUIVisualizationSettings::getLinkColor(state));541switch (state) {542case LINKSTATE_TL_RED:543case LINKSTATE_TL_REDYELLOW:544// draw a thin line545glBegin(GL_QUADS);546glVertex2d(x, h - h11);547glVertex2d(x, h - h9);548glVertex2d(x2, h - h9);549glVertex2d(x2, h - h11);550glEnd();551break;552default:553// draw a thick block554glBegin(GL_QUADS);555glVertex2d(x, h - stateHeight);556glVertex2d(x, h);557glVertex2d(x2, h);558glVertex2d(x2, h - stateHeight);559glEnd();560break;561}562if (myGreenMode->getCurrentItem() != 0) {563SUMOTime drawnDuration = 0;564double xOffset = 0;565if (state == LINKSTATE_TL_GREEN_MINOR || state == LINKSTATE_TL_GREEN_MAJOR) {566if (myGreenMode->getCurrentItem() == 1) {567drawnDuration = *pd;568} else {569runningGreen[j] += *pd;570if (pd + 1 == myDurations.end()) {571drawnDuration = runningGreen[j];572xOffset = -(double)(drawnDuration - *pd) / panelWidth * (barWidth / ((double)(myLastTime - myBeginTime)));573}574}575} else {576if (runningGreen[j] > 0) {577drawnDuration = runningGreen[j];578xOffset = -(double)drawnDuration / panelWidth * (barWidth / ((double)(myLastTime - myBeginTime)));579}580runningGreen[j] = 0;581}582if (drawnDuration > 0) {583GLHelper::drawText(toString((int)STEPS2TIME(drawnDuration)),584Position(x + xOffset, h - h9),5850, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);586}587}588// proceed to next link589h -= h20;590}591592// draw phase index / name (no names for intermediate)593std::string name = phaseNames ? pi->getName() : toString(*ii);594if (name != lastName) {595const double lastNameWidth = GLHelper::getTextWidth(lastName, fontWidth);596if (spaceForName < lastNameWidth) {597// clear space to avoid overdrawn text598glColor3d(0, 0, 0);599glBegin(GL_QUADS);600glVertex2d(x, 1 - fontHeight);601glVertex2d(x, 1);602glVertex2d(1, 1);603glVertex2d(1, 1 - fontHeight);604glEnd();605}606spaceForName = a;607GLHelper::drawText(name, Position(x, 1 - hTop), 0, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);608} else {609spaceForName += a;610}611lastName = name;612// proceed to next phase613++pi;614++ii;615x = x2;616// all further phases are drawn in full617fpo = 0;618}619620if (myAmInTrackingMode) {621h -= h75;622if (myDetectorMode->getCheck()) {623glColor3d(0.7, 0.7, 1.0);624drawAdditionalStates(caller, myDetectorStates, myDetectorDurations, myFirstDetOffset, myFirstDet2Show, h,625panelWidth, (double)leftOffset, barWidth, stateHeight, h20, h);626h -= h35;627}628if (myConditionMode->getCheck()) {629glColor3d(0.9, 0.6, 0.9);630drawAdditionalStates(caller, myConditionStates, myConditionDurations, myFirstCondOffset, myFirstCond2Show, h,631panelWidth, (double)leftOffset, barWidth, stateHeight, h20, h);632}633}634// allow value addition635myLock.unlock();636637if (myPhases.size() != 0) {638const double timeRange = STEPS2TIME(myLastTime - myBeginTime);639SUMOTime tickDist = TIME2STEPS(10);640// patch distances - hack641double t = myBeginOffset != nullptr ? myBeginOffset->getValue() : timeRange;642while (t > barWidth / 4.) {643tickDist += TIME2STEPS(10);644t -= barWidth / 4.;645}646// draw time information647//h = (double)(myTLLogic->getLinks().size() * 20 + 12);648double glh = 1. - (double)myTLLogic->getLinks().size() * h20 - hTop;649// current begin time650// time ticks651SUMOTime currTime = myFirstTime2Show;652double glpos = 31. / panelWidth;653const double ticSize = 4. / panelHeight;654if (leftOffset > 0) {655const double a = STEPS2TIME(leftOffset) * barWidth / timeRange;656glpos += a / panelWidth;657currTime += leftOffset;658} else if (myFirstPhaseOffset > 0) {659const double a = STEPS2TIME(-myFirstPhaseOffset) * barWidth / timeRange;660glpos += a / panelWidth;661currTime -= myFirstPhaseOffset;662}663int ticShift = myFirstPhase2Show;664const bool mmSS = myTimeMode->getCurrentItem() == 1;665const bool cycleTime = myTimeMode->getCurrentItem() == 2;666SUMOTime lastTimeInCycle = -1;667lastName = "";668pi = myPhases.begin() + myFirstPhase2Show;669for (DurationsVector::iterator pd = myDurations.begin() + myFirstPhase2Show; pd != myDurations.end(); ++pd) {670const SUMOTime timeInCycle = myTimeInCycle[pd - myDurations.begin()];671// draw times at different heights672ticShift = (ticShift % 3) + 1;673const std::string timeStr = (mmSS674? StringUtils::padFront(toString((currTime % 3600000) / 60000), 2, '0') + ":"675+ StringUtils::padFront(toString((currTime % 60000) / 1000), 2, '0')676: toString((int)STEPS2TIME(cycleTime ? timeInCycle : currTime)));677const double w = 10 * (double)timeStr.size() / panelWidth;678glTranslated(glpos - w / 2., glh - h20 * ticShift, 0);679GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);680glTranslated(-glpos + w / 2., -glh + h20 * ticShift, 0);681682// draw tic683glColor3d(1, 1, 1);684glBegin(GL_LINES);685glVertex2d(glpos, glh);686glVertex2d(glpos, glh - ticSize * ticShift);687glEnd();688689// draw vertical lines for names, detectors and conditions on each phase switch690if (myAmInTrackingMode) {691double hStart = 1;692if (!phaseNames || (pi->getName() != lastName)) {693glColor3d(0.4, 0.4, 0.4);694glBegin(GL_LINES);695glVertex2d(glpos, hStart);696hStart -= h20;697glVertex2d(glpos, hStart);698glEnd();699}700lastName = pi->getName();701702hStart = glh - h60;703if (myDetectorMode->getCheck() && glpos >= w30) {704glColor3d(0.4, 0.4, 0.4);705glBegin(GL_LINES);706glVertex2d(glpos, hStart);707hStart -= (double)myDetectorNames.size() * h20;708glVertex2d(glpos, hStart);709glEnd();710hStart -= h35;711}712if (myConditionMode->getCheck() && glpos >= w30) {713glColor3d(0.4, 0.4, 0.4);714glBegin(GL_LINES);715glVertex2d(glpos, hStart);716glVertex2d(glpos, hStart - (double)myConditionNames.size() * h20);717glEnd();718}719}720721// draw vertical line for cycle reset722if (timeInCycle == 0 || timeInCycle < lastTimeInCycle) {723const double cycle0pos = glpos - STEPS2TIME(timeInCycle) * barWidth / timeRange / panelWidth;724if (cycle0pos >= 31 / panelWidth) {725glColor3d(0.6, 0.6, 0.6);726glBegin(GL_LINES);727glVertex2d(cycle0pos, 1);728glVertex2d(cycle0pos, glh);729glEnd();730glColor3d(1, 1, 1);731}732}733734lastTimeInCycle = timeInCycle;735tickDist = *pd;736const double a = STEPS2TIME(tickDist) * barWidth / timeRange;737glpos += a / panelWidth;738currTime += tickDist;739++pi;740}741742// draw bottom time bar with fixed spacing743if (myAmInTrackingMode && (myDetectorMode->getCheck() || myConditionMode->getCheck()) && glpos >= w30) {744glColor3d(1, 1, 1);745tickDist = TIME2STEPS(10);746// patch distances - hack747t = myBeginOffset != nullptr ? myBeginOffset->getValue() : STEPS2TIME(myLastTime - myBeginTime);748while (t > barWidth / 4.) {749tickDist += TIME2STEPS(10);750t -= barWidth / 4.;751}752glh = 1. - (double)myLinkNames.size() * h20 - h80;753glh -= h20 * (double)(myDetectorMode->getCheck() ? myDetectorNames.size() : myConditionNames.size());754currTime = myFirstTime2Show;755int pos = 31;756glpos = (double) pos / panelWidth;757if (leftOffset > 0) {758const double a = STEPS2TIME(leftOffset) * barWidth / timeRange;759pos += (int)a;760glpos += a / panelWidth;761currTime += leftOffset;762} else if (myFirstPhaseOffset > 0) {763const double a = -STEPS2TIME(myBeginTime % tickDist) * barWidth / timeRange;764pos += (int)a;765glpos += a / panelWidth;766currTime = myBeginTime - (myBeginTime % tickDist);767}768while (pos < panelWidth + 50.) {769const std::string timeStr = (mmSS770? StringUtils::padFront(toString((currTime % 3600000) / 60000), 2, '0') + ":"771+ StringUtils::padFront(toString((currTime % 60000) / 1000), 2, '0')772: toString((int)STEPS2TIME(cycleTime ? findTimeInCycle(currTime) : currTime)));773const double w = 10. * (double)timeStr.size() / panelWidth;774glTranslated(glpos - w / 2., glh - h20, 0);775GLHelper::drawText(timeStr, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, fontWidth);776glTranslated(-glpos + w / 2., -glh + h20, 0);777778glBegin(GL_LINES);779glVertex2d(glpos, glh);780glVertex2d(glpos, glh - ticSize);781glEnd();782783const double a = STEPS2TIME(tickDist) * barWidth / STEPS2TIME(myLastTime - myBeginTime);784pos += (int) a;785glpos += a / panelWidth;786currTime += tickDist;787}788}789}790}791792793void794GUITLLogicPhasesTrackerWindow::drawNames(const std::vector<std::string>& names, double fontHeight, double fontWidth, double divHeight, double divWidth, double& h, int extraLines) {795int i = 0;796for (const std::string& name : names) {797// draw the bar798glBegin(GL_LINES);799glVertex2d(0, h);800glVertex2d(divWidth, h);801glEnd();802// draw the name803glTranslated(0, h - divHeight, 0);804GLHelper::drawText(name, Position(0, 0), 1, fontHeight, RGBColor::WHITE, 0, FONS_ALIGN_LEFT | FONS_ALIGN_BOTTOM, fontWidth);805glTranslated(0, -h + divHeight, 0);806807if (extraLines > 0 && i > 0 && i % extraLines == 0) {808glColor3d(0.4, 0.4, 0.4);809glBegin(GL_LINES);810glVertex2d(divWidth, h);811glVertex2d(1.0, h);812glEnd();813glColor3d(1, 1, 1);814}815h -= divHeight;816i++;817}818h -= divHeight;819}820821822void823GUITLLogicPhasesTrackerWindow::drawAdditionalStates(GUITLLogicPhasesTrackerPanel& caller,824const AdditionalStatesVector& states,825const DurationsVector& durations, SUMOTime firstOffset, int first2Show, double hStart,826double panelWidth, double leftOffset, double barWidth, double stateHeight, double h20, double& h) {827double x = 31. / panelWidth;828double ta = leftOffset / panelWidth;829ta *= barWidth / ((double)(myLastTime - myBeginTime));830x += ta;831auto di = states.begin() + first2Show;832SUMOTime fpo = firstOffset;833834double mx = caller.getMousePos().x() / caller.getWidth();835double my = 1 - caller.getMousePos().y() / caller.getHeight();836std::string tooltip = "";837// start drawing838for (auto pd = durations.begin() + first2Show; pd != durations.end(); ++pd) {839// the first phase may be drawn incompletely840SUMOTime duration = *pd - fpo;841// compute the height and the width of the phase842h = hStart;843double a = (double) duration / panelWidth;844a *= barWidth / ((double)(myLastTime - myBeginTime));845const double x2 = x + a;846const bool tooltipX = x < mx && mx < x2;847//std::cout << SIMTIME << " detStates=" << toString(*di) << "\n";848// go through the detectors849for (double j : *di) {850if (j != 0) {851// draw a thick block852glBegin(GL_QUADS);853glVertex2d(x, h - stateHeight);854glVertex2d(x, h);855glVertex2d(x2, h);856glVertex2d(x2, h - stateHeight);857glEnd();858if (tooltipX) {859const bool tooltipY = (h - stateHeight) < my && my < h;860if (tooltipY) {861tooltip = toString((int)j);862}863}864}865// proceed to next link866h -= h20;867}868// proceed to next phase869++di;870x = x2;871// all further phases are drawn in full872fpo = 0;873}874if (tooltip != "") {875// delay tool tip drawing until all bars are drawn to prevent overwriting876GLHelper::drawText(tooltip, Position(mx, my), 0, h20, RGBColor::YELLOW, 0, FONS_ALIGN_LEFT | FONS_ALIGN_MIDDLE, 20 / caller.getWidth());877}878}879880SUMOTime881GUITLLogicPhasesTrackerWindow::findTimeInCycle(SUMOTime t) {882// find latest cycle reset before t883int i = (int)myPhases.size() - 1;884SUMOTime lookBack = myLastTime - t - myDurations.back();885//std::cout << SIMTIME << " findTimeInCycle t=" << STEPS2TIME(t)886// << " last=" << STEPS2TIME(myLastTime)887// << " lastDur=" << STEPS2TIME(myDurations.back())888// << " lookBack=" << STEPS2TIME(lookBack)889// << " i0=" << i;890// look backwards through the phases until to the first cycle crossing before t891while (lookBack > 0 && i > 1) {892i--;893lookBack -= myDurations[i];894}895SUMOTime timeInCycle = myTimeInCycle[i < 0 ? 0 : i];896//std::cout << " iF=" << i << " lookBack2=" << STEPS2TIME(lookBack) << " tic=" << STEPS2TIME(timeInCycle) << "\n";897if (lookBack <= 0) {898return timeInCycle - lookBack;899}900return myTLLogic->mapTimeInCycle(t);901}902903void904GUITLLogicPhasesTrackerWindow::addValue(std::pair<SUMOTime, MSPhaseDefinition> def) {905// do not draw while adding906myLock.lock();907// set the first time if not set before908if (myPhases.size() == 0) {909myBeginTime = def.first;910}911// append or set the phase912if (myPhases.size() == 0 || myPhases.back() != def.second) {913myPhases.push_back(def.second);914myDurations.push_back(DELTA_T);915myTimeInCycle.push_back(myTLLogic->mapTimeInCycle(def.first - DELTA_T));916myPhaseIndex.push_back(myTLLogic->getCurrentPhaseIndex());917} else {918myDurations.back() += DELTA_T;919}920// updated detector states921std::vector<double> detectorStates;922for (auto item : myTLLogic->getDetectorStates()) {923detectorStates.push_back(item.second);924}925if (myDetectorStates.size() == 0 || myDetectorStates.back() != detectorStates) {926myDetectorStates.push_back(detectorStates);927myDetectorDurations.push_back(DELTA_T);928} else {929myDetectorDurations.back() += DELTA_T;930}931// updated condition states932std::vector<double> conditionStates;933for (auto item : myTLLogic->getConditions()) {934conditionStates.push_back(item.second);935}936if (myConditionStates.size() == 0 || myConditionStates.back() != conditionStates) {937myConditionStates.push_back(conditionStates);938myConditionDurations.push_back(DELTA_T);939} else {940myConditionDurations.back() += DELTA_T;941}942// set the last time a phase was added at943myLastTime = def.first;944// allow drawing945myLock.unlock();946}947948949long950GUITLLogicPhasesTrackerWindow::onConfigure(FXObject* sender, FXSelector sel, void* ptr) {951myPanel->onConfigure(sender, sel, ptr);952return FXMainWindow::onConfigure(sender, sel, ptr);953}954955956long957GUITLLogicPhasesTrackerWindow::onPaint(FXObject* sender, FXSelector sel, void* ptr) {958myPanel->onPaint(sender, sel, ptr);959return FXMainWindow::onPaint(sender, sel, ptr);960}961962963long964GUITLLogicPhasesTrackerWindow::onSimStep(FXObject* sender, FXSelector, void*) {965if (sender == myDetectorMode || sender == myConditionMode) {966resize(getWidth(), computeHeight());967}968update();969return 1;970}971972973void974GUITLLogicPhasesTrackerWindow::setBeginTime(SUMOTime time) {975myBeginTime = time;976}977978979void980GUITLLogicPhasesTrackerWindow::saveSettings() {981getApp()->reg().writeIntEntry("TL_TRACKER", "x", getX());982getApp()->reg().writeIntEntry("TL_TRACKER", "y", getY());983getApp()->reg().writeIntEntry("TL_TRACKER", "width", getWidth());984getApp()->reg().writeIntEntry("TL_TRACKER", "timeRange", (int)myBeginOffset->getValue());985getApp()->reg().writeIntEntry("TL_TRACKER", "timeMode", myTimeMode->getCurrentItem());986getApp()->reg().writeIntEntry("TL_TRACKER", "greenMode", (myGreenMode->getCurrentItem()));987getApp()->reg().writeIntEntry("TL_TRACKER", "indexMode", (int)(myIndexMode->getCheck()));988getApp()->reg().writeIntEntry("TL_TRACKER", "detectorMode", (int)(myDetectorMode->getCheck()));989getApp()->reg().writeIntEntry("TL_TRACKER", "conditionMode", (int)(myConditionMode->getCheck()));990}991992993void994GUITLLogicPhasesTrackerWindow::loadSettings() {995// ensure window is visible after switching screen resolutions996const FXint minSize = 400;997const FXint minTitlebarHeight = 20;998setX(MAX2(0, MIN2(getApp()->reg().readIntEntry("TL_TRACKER", "x", 150),999getApp()->getRootWindow()->getWidth() - minSize)));1000if (myLastY == -1) {1001myLastY = MAX2(minTitlebarHeight,1002MIN2(getApp()->reg().readIntEntry("TL_TRACKER", "y", 150),1003getApp()->getRootWindow()->getHeight() - minSize));1004} else {1005myLastY += getHeight() + 20;1006}1007setY(myLastY);1008setWidth(MAX2(getApp()->reg().readIntEntry("TL_TRACKER", "width", 700), minSize));1009myBeginOffset->setValue(getApp()->reg().readIntEntry("TL_TRACKER", "timeRange", (int)myBeginOffset->getValue()));1010myTimeMode->setCurrentItem(getApp()->reg().readIntEntry("TL_TRACKER", "timeMode", myTimeMode->getCurrentItem()));1011myGreenMode->setCurrentItem(getApp()->reg().readIntEntry("TL_TRACKER", "greenMode", myGreenMode->getCurrentItem()));1012myIndexMode->setCheck((FXbool)getApp()->reg().readIntEntry("TL_TRACKER", "indexMode", (int)(myIndexMode->getCheck())));1013myDetectorMode->setCheck((FXbool)getApp()->reg().readIntEntry("TL_TRACKER", "detectorMode", (int)(myDetectorMode->getCheck())));1014myConditionMode->setCheck((FXbool)getApp()->reg().readIntEntry("TL_TRACKER", "conditionMode", (int)(myConditionMode->getCheck())));1015}10161017/****************************************************************************/101810191020