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 GNETLSEditorFrame.cpp
11 /// @author Jakob Erdmann
12 /// @date May 2011
13 /// @version $Id$
14 ///
15 // The Widget for modifying traffic lights
16 /****************************************************************************/
17
18
19 // ===========================================================================
20 // included modules
21 // ===========================================================================
22 #include <config.h>
23
24 #include <utils/foxtools/MFXUtils.h>
25 #include <utils/gui/windows/GUIAppEnum.h>
26 #include <utils/gui/div/GUIDesigns.h>
27 #include <netbuild/NBLoadedSUMOTLDef.h>
28 #include <utils/gui/images/GUIIconSubSys.h>
29 #include <utils/xml/XMLSubSys.h>
30 #include <netimport/NIXMLTrafficLightsHandler.h>
31 #include <netedit/changes/GNEChange_TLS.h>
32 #include <netedit/GNEViewNet.h>
33 #include <netedit/GNENet.h>
34 #include <netedit/netelements/GNEJunction.h>
35 #include <netedit/netelements/GNEEdge.h>
36 #include <netedit/netelements/GNELane.h>
37 #include <netedit/GNEUndoList.h>
38 #include <netedit/netelements/GNEInternalLane.h>
39 #include <utils/options/OptionsCont.h>
40
41 #include "GNETLSEditorFrame.h"
42
43 // ===========================================================================
44 // FOX callback mapping
45 // ===========================================================================
46
47 FXDEFMAP(GNETLSEditorFrame) GNETLSEditorFrameMap[] = {
48 FXMAPFUNC(SEL_COMMAND, MID_CANCEL, GNETLSEditorFrame::onCmdCancel),
49 FXMAPFUNC(SEL_UPDATE, MID_CANCEL, GNETLSEditorFrame::onUpdModified),
50 FXMAPFUNC(SEL_COMMAND, MID_OK, GNETLSEditorFrame::onCmdOK),
51 FXMAPFUNC(SEL_UPDATE, MID_OK, GNETLSEditorFrame::onUpdModified),
52 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_CREATE, GNETLSEditorFrame::onCmdDefCreate),
53 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_CREATE, GNETLSEditorFrame::onUpdDefCreate),
54 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_DELETE, GNETLSEditorFrame::onCmdDefDelete),
55 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_DELETE, GNETLSEditorFrame::onUpdDefSwitch),
56 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_SWITCH, GNETLSEditorFrame::onCmdDefSwitch),
57 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_SWITCH, GNETLSEditorFrame::onUpdDefSwitch),
58 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_OFFSET, GNETLSEditorFrame::onCmdDefOffset),
59 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_OFFSET, GNETLSEditorFrame::onUpdNeedsDef),
60 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_RENAME, GNETLSEditorFrame::onCmdDefRename),
61 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_SUBRENAME, GNETLSEditorFrame::onCmdDefSubRename),
62 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_ADDOFF, GNETLSEditorFrame::onCmdDefAddOff),
63 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_GUESSPROGRAM, GNETLSEditorFrame::onCmdGuess),
64 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_PHASE_CREATE, GNETLSEditorFrame::onCmdPhaseCreate),
65 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_PHASE_CREATE, GNETLSEditorFrame::onUpdNeedsDef),
66 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_PHASE_DELETE, GNETLSEditorFrame::onCmdPhaseDelete),
67 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_PHASE_DELETE, GNETLSEditorFrame::onUpdNeedsDefAndPhase),
68 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_CLEANUP, GNETLSEditorFrame::onCmdCleanup),
69 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_CLEANUP, GNETLSEditorFrame::onUpdNeedsDef),
70 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_ADDUNUSED, GNETLSEditorFrame::onCmdAddUnused),
71 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_ADDUNUSED, GNETLSEditorFrame::onUpdNeedsDef),
72 FXMAPFUNC(SEL_SELECTED, MID_GNE_TLSFRAME_PHASE_TABLE, GNETLSEditorFrame::onCmdPhaseSwitch),
73 FXMAPFUNC(SEL_DESELECTED, MID_GNE_TLSFRAME_PHASE_TABLE, GNETLSEditorFrame::onCmdPhaseSwitch),
74 FXMAPFUNC(SEL_CHANGED, MID_GNE_TLSFRAME_PHASE_TABLE, GNETLSEditorFrame::onCmdPhaseSwitch),
75 FXMAPFUNC(SEL_REPLACED, MID_GNE_TLSFRAME_PHASE_TABLE, GNETLSEditorFrame::onCmdPhaseEdit),
76 };
77
78 FXDEFMAP(GNETLSEditorFrame::TLSFile) TLSFileMap[] = {
79 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_LOAD_PROGRAM, GNETLSEditorFrame::TLSFile::onCmdLoadTLSProgram),
80 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_LOAD_PROGRAM, GNETLSEditorFrame::TLSFile::onUpdNeedsDef),
81 FXMAPFUNC(SEL_COMMAND, MID_GNE_TLSFRAME_SAVE_PROGRAM, GNETLSEditorFrame::TLSFile::onCmdSaveTLSProgram),
82 FXMAPFUNC(SEL_UPDATE, MID_GNE_TLSFRAME_SAVE_PROGRAM, GNETLSEditorFrame::TLSFile::onUpdNeedsDef),
83 };
84
85 // Object implementation
FXIMPLEMENT(GNETLSEditorFrame,FXVerticalFrame,GNETLSEditorFrameMap,ARRAYNUMBER (GNETLSEditorFrameMap))86 FXIMPLEMENT(GNETLSEditorFrame, FXVerticalFrame, GNETLSEditorFrameMap, ARRAYNUMBER(GNETLSEditorFrameMap))
87 FXIMPLEMENT(GNETLSEditorFrame::TLSFile, FXGroupBox, TLSFileMap, ARRAYNUMBER(TLSFileMap))
88
89
90 // ===========================================================================
91 // method definitions
92 // ===========================================================================
93
94 GNETLSEditorFrame::GNETLSEditorFrame(FXHorizontalFrame* horizontalFrameParent, GNEViewNet* viewNet):
95 GNEFrame(horizontalFrameParent, viewNet, "Edit Traffic Light"),
96 myEditedDef(nullptr) {
97
98 // create TLSJunction modul
99 myTLSJunction = new GNETLSEditorFrame::TLSJunction(this);
100
101 // create TLSDefinition modul
102 myTLSDefinition = new GNETLSEditorFrame::TLSDefinition(this);
103
104 // create TLSAttributes modul
105 myTLSAttributes = new GNETLSEditorFrame::TLSAttributes(this);
106
107 // create TLSModifications modul
108 myTLSModifications = new GNETLSEditorFrame::TLSModifications(this);
109
110 // create TLSPhases modul
111 myTLSPhases = new GNETLSEditorFrame::TLSPhases(this);
112
113 // create TLSFile modul
114 myTLSFile = new GNETLSEditorFrame::TLSFile(this);
115
116 // "Add 'off' program"
117 /*
118 new FXButton(myContentFrame, "Add \"Off\"-Program\t\tAdds a program for switching off this traffic light",
119 0, this, MID_GNE_TLSFRAME_ADDOFF, GUIDesignButton);
120 */
121 }
122
123
~GNETLSEditorFrame()124 GNETLSEditorFrame::~GNETLSEditorFrame() {
125 cleanup();
126 }
127
128
129 void
editJunction(GNEJunction * junction)130 GNETLSEditorFrame::editJunction(GNEJunction* junction) {
131 if (myTLSJunction->getCurrentJunction() == nullptr || (!myTLSModifications->checkHaveModifications() && (junction != myTLSJunction->getCurrentJunction()))) {
132 onCmdCancel(nullptr, 0, nullptr);
133 myViewNet->getUndoList()->p_begin("modifying traffic light definition");
134 myTLSJunction->setCurrentJunction(junction);
135 myTLSAttributes->initTLSAttributes(myTLSJunction->getCurrentJunction());
136 myTLSJunction->updateJunctionDescription();
137 myTLSJunction->getCurrentJunction()->selectTLS(true);
138 if (myTLSAttributes->getNumberOfTLSDefinitions() > 0) {
139 for (NBNode* node : myTLSAttributes->getCurrentTLSDefinition()->getNodes()) {
140 myViewNet->getNet()->retrieveJunction(node->getID())->selectTLS(true);
141 }
142 }
143 } else {
144 myViewNet->setStatusBarText("Unsaved modifications. Abort or Save");
145 }
146 }
147
148
149 bool
isTLSSaved()150 GNETLSEditorFrame::isTLSSaved() {
151 if (myTLSModifications->checkHaveModifications()) {
152 // write warning if netedit is running in testing mode
153 WRITE_DEBUG("Opening question FXMessageBox 'save TLS'");
154 // open question box
155 FXuint answer = FXMessageBox::question(this, MBOX_YES_NO_CANCEL,
156 "Save TLS Changes", "%s",
157 "There is unsaved changes in current edited traffic light.\nDo you want to save it before changing mode?");
158 if (answer == MBOX_CLICKED_YES) { //1:yes, 2:no, 4:esc/cancel
159 // write warning if netedit is running in testing mode
160 WRITE_DEBUG("Closed FXMessageBox 'save TLS' with 'YES'");
161 // save modifications
162 onCmdOK(nullptr, 0, nullptr);
163 return true;
164 } else if (answer == MBOX_CLICKED_NO) {
165 // write warning if netedit is running in testing mode
166 WRITE_DEBUG("Closed FXMessageBox 'save TLS' with 'No'");
167 // cancel modifications
168 onCmdCancel(nullptr, 0, nullptr);
169 return true;
170 } else {
171 // write warning if netedit is running in testing mode
172 WRITE_DEBUG("Closed FXMessageBox 'save TLS' with 'Cancel'");
173 // abort changing mode
174 return false;
175 }
176 } else {
177 return true;
178 }
179 }
180
181
182 bool
parseTLSPrograms(const std::string & file)183 GNETLSEditorFrame::parseTLSPrograms(const std::string& file) {
184 NBTrafficLightLogicCont& tllCont = myViewNet->getNet()->getTLLogicCont();
185 NBTrafficLightLogicCont tmpTLLCont;;
186 NIXMLTrafficLightsHandler tllHandler(tmpTLLCont, myViewNet->getNet()->getEdgeCont());
187 // existing definitions must be available to update their programs
188 std::set<NBTrafficLightDefinition*> origDefs;
189 for (NBTrafficLightDefinition* def : tllCont.getDefinitions()) {
190 // make a copy of every program
191 NBTrafficLightLogic* logic = tllCont.getLogic(def->getID(), def->getProgramID());
192 if (logic != nullptr) {
193 NBTrafficLightDefinition* copy = new NBLoadedSUMOTLDef(def, logic);
194 std::vector<NBNode*> nodes = def->getNodes();
195 for (auto it_node : nodes) {
196 GNEJunction* junction = myViewNet->getNet()->retrieveJunction(it_node->getID());
197 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, def, false, false), true);
198 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, copy, true), true);
199 }
200 tmpTLLCont.insert(copy);
201 origDefs.insert(copy);
202 } else {
203 WRITE_WARNING("tlLogic '" + def->getID() + "', program '" + def->getProgramID() + "' could not be built");
204 }
205 }
206 //std::cout << " initialized tmpCont with " << origDefs.size() << " defs\n";
207 XMLSubSys::runParser(tllHandler, file);
208
209 std::vector<NBLoadedSUMOTLDef*> loadedTLS;
210 for (NBTrafficLightDefinition* def : tmpTLLCont.getDefinitions()) {
211 NBLoadedSUMOTLDef* sdef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
212 if (sdef != nullptr) {
213 loadedTLS.push_back(sdef);
214 }
215 }
216 myViewNet->setStatusBarText("Loaded " + toString(loadedTLS.size()) + " programs");
217 for (auto def : loadedTLS) {
218 if (origDefs.count(def) != 0) {
219 // already add to undolist before
220 //std::cout << " skip " << def->getDescription() << "\n";
221 continue;
222 }
223 std::vector<NBNode*> nodes = def->getNodes();
224 //std::cout << " add " << def->getDescription() << " for nodes=" << toString(nodes) << "\n";
225 for (auto it_node : nodes) {
226 GNEJunction* junction = myViewNet->getNet()->retrieveJunction(it_node->getID());
227 //myViewNet->getUndoList()->add(new GNEChange_TLS(junction, myTLSEditorParent->myEditedDef, false), true);
228 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, def, true), true);
229 }
230 }
231 // clean up temporary container to avoid deletion of defs when it's destruct is called
232 for (NBTrafficLightDefinition* def : tmpTLLCont.getDefinitions()) {
233 tmpTLLCont.removeProgram(def->getID(), def->getProgramID(), false);
234 }
235 return true;
236 }
237
238
239 long
onCmdCancel(FXObject *,FXSelector,void *)240 GNETLSEditorFrame::onCmdCancel(FXObject*, FXSelector, void*) {
241 if (myTLSJunction->getCurrentJunction() != nullptr) {
242 myViewNet->getUndoList()->p_abort();
243 cleanup();
244 myViewNet->update();
245 }
246 return 1;
247 }
248
249
250 long
onCmdOK(FXObject *,FXSelector,void *)251 GNETLSEditorFrame::onCmdOK(FXObject*, FXSelector, void*) {
252 if (myTLSJunction->getCurrentJunction() != nullptr) {
253 if (myTLSModifications->checkHaveModifications()) {
254 NBTrafficLightDefinition* oldDefinition = myTLSAttributes->getCurrentTLSDefinition();
255 std::vector<NBNode*> nodes = oldDefinition->getNodes();
256 for (auto it : nodes) {
257 GNEJunction* junction = myViewNet->getNet()->retrieveJunction(it->getID());
258 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, oldDefinition, false), true);
259 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, myEditedDef, true), true);
260 }
261 myEditedDef = nullptr;
262 myViewNet->getUndoList()->p_end();
263 cleanup();
264 myViewNet->update();
265 } else {
266 onCmdCancel(nullptr, 0, nullptr);
267 }
268 }
269 return 1;
270 }
271
272
273 long
onCmdDefCreate(FXObject *,FXSelector,void *)274 GNETLSEditorFrame::onCmdDefCreate(FXObject*, FXSelector, void*) {
275 GNEJunction* junction = myTLSJunction->getCurrentJunction();
276 // abort because we onCmdOk assumes we wish to save an edited definition
277 onCmdCancel(nullptr, 0, nullptr);
278 // check that current junction has two or more edges
279 if ((junction->getGNEIncomingEdges().size() > 0) && (junction->getGNEOutgoingEdges().size() > 0)) {
280 if (junction->getAttribute(SUMO_ATTR_TYPE) != toString(NODETYPE_TRAFFIC_LIGHT)) {
281 junction->setAttribute(SUMO_ATTR_TYPE, toString(NODETYPE_TRAFFIC_LIGHT), myViewNet->getUndoList());
282 } else {
283 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, nullptr, true, true), true);
284 }
285 editJunction(junction);
286 } else {
287 // write warning if netedit is running in testing mode
288 WRITE_DEBUG("Opening warning FXMessageBox 'invalid TLS'");
289 // open question box
290 FXMessageBox::warning(this, MBOX_OK,
291 "TLS cannot be created", "%s",
292 "Traffic Light cannot be created because junction must have\n at least one incoming edge and one outgoing edge.");
293 // write warning if netedit is running in testing mode
294 WRITE_DEBUG("Closed FXMessageBox 'invalid TLS'");
295 }
296 return 1;
297 }
298
299
300 long
onCmdDefDelete(FXObject *,FXSelector,void *)301 GNETLSEditorFrame::onCmdDefDelete(FXObject*, FXSelector, void*) {
302 GNEJunction* junction = myTLSJunction->getCurrentJunction();
303 const bool changeType = myTLSAttributes->getNumberOfTLSDefinitions() == 1;
304 NBTrafficLightDefinition* tlDef = myTLSAttributes->getCurrentTLSDefinition();
305 onCmdCancel(nullptr, 0, nullptr); // abort because onCmdOk assumes we wish to save an edited definition
306 if (changeType) {
307 junction->setAttribute(SUMO_ATTR_TYPE, toString(NODETYPE_PRIORITY), myViewNet->getUndoList());
308 } else {
309 myViewNet->getUndoList()->add(new GNEChange_TLS(junction, tlDef, false), true);
310 }
311 return 1;
312 }
313
314
315 long
onCmdDefSwitch(FXObject *,FXSelector,void *)316 GNETLSEditorFrame::onCmdDefSwitch(FXObject*, FXSelector, void*) {
317 assert(myTLSJunction->getCurrentJunction() != 0);
318 assert(myTLSAttributes->getNumberOfTLSDefinitions() == myTLSAttributes->getNumberOfPrograms());
319 NBTrafficLightDefinition* tlDef = myTLSAttributes->getCurrentTLSDefinition();
320 // logic may not have been recomputed yet. recompute to be sure
321 NBTrafficLightLogicCont& tllCont = myViewNet->getNet()->getTLLogicCont();
322 myViewNet->getNet()->computeJunction(myTLSJunction->getCurrentJunction());
323 NBTrafficLightLogic* tllogic = tllCont.getLogic(tlDef->getID(), tlDef->getProgramID());
324 if (tllogic != nullptr) {
325 // now we can be sure that the tlDef is up to date (i.e. re-guessed)
326 buildIinternalLanes(tlDef);
327 // create working copy from original def
328 delete myEditedDef;
329 myEditedDef = new NBLoadedSUMOTLDef(tlDef, tllogic);
330 myTLSAttributes->setOffset(myEditedDef->getLogic()->getOffset());
331 myTLSPhases->initPhaseTable();
332 myTLSPhases->updateCycleDuration();
333 myTLSPhases->showCycleDuration();
334 } else {
335 // tlDef has no valid logic (probably because id does not control any links
336 onCmdCancel(nullptr, 0, nullptr);
337 myViewNet->setStatusBarText("Traffic light does not control any links");
338 }
339 return 1;
340 }
341
342
343 long
onUpdDefSwitch(FXObject * o,FXSelector,void *)344 GNETLSEditorFrame::onUpdDefSwitch(FXObject* o, FXSelector, void*) {
345 const bool enable = myTLSAttributes->getNumberOfTLSDefinitions() > 0 && !myTLSModifications->checkHaveModifications();
346 o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
347 return 1;
348 }
349
350
351 long
onUpdNeedsDef(FXObject * o,FXSelector,void *)352 GNETLSEditorFrame::onUpdNeedsDef(FXObject* o, FXSelector, void*) {
353 const bool enable = myTLSAttributes->getNumberOfTLSDefinitions() > 0;
354 o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
355 return 1;
356 }
357
358
359 long
onUpdNeedsDefAndPhase(FXObject * o,FXSelector,void *)360 GNETLSEditorFrame::onUpdNeedsDefAndPhase(FXObject* o, FXSelector, void*) {
361 // do not delete the last phase
362 const bool enable = myTLSAttributes->getNumberOfTLSDefinitions() > 0 && myTLSPhases->getPhaseTable()->getNumRows() > 1;
363 o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
364 return 1;
365 }
366
367
368 long
onUpdDefCreate(FXObject * o,FXSelector,void *)369 GNETLSEditorFrame::onUpdDefCreate(FXObject* o, FXSelector, void*) {
370 const bool enable = myTLSJunction->getCurrentJunction() != nullptr && !myTLSModifications->checkHaveModifications();
371 o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
372 return 1;
373 }
374
375
376 long
onUpdModified(FXObject * o,FXSelector,void *)377 GNETLSEditorFrame::onUpdModified(FXObject* o, FXSelector, void*) {
378 bool enable = myTLSModifications->checkHaveModifications();
379 o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
380 return 1;
381 }
382
383
384
385 long
onCmdDefOffset(FXObject *,FXSelector,void *)386 GNETLSEditorFrame::onCmdDefOffset(FXObject*, FXSelector, void*) {
387 myTLSModifications->setHaveModifications(true);
388 myEditedDef->setOffset(myTLSAttributes->getOffset());
389 return 1;
390 }
391
392
393 long
onCmdDefRename(FXObject *,FXSelector,void *)394 GNETLSEditorFrame::onCmdDefRename(FXObject*, FXSelector, void*) {
395 return 1;
396 }
397
398
399 long
onCmdDefSubRename(FXObject *,FXSelector,void *)400 GNETLSEditorFrame::onCmdDefSubRename(FXObject*, FXSelector, void*) {
401 return 1;
402 }
403
404
405 long
onCmdDefAddOff(FXObject *,FXSelector,void *)406 GNETLSEditorFrame::onCmdDefAddOff(FXObject*, FXSelector, void*) {
407 return 1;
408 }
409
410
411 long
onCmdGuess(FXObject *,FXSelector,void *)412 GNETLSEditorFrame::onCmdGuess(FXObject*, FXSelector, void*) {
413 return 1;
414 }
415
416
417 long
onCmdPhaseSwitch(FXObject *,FXSelector,void *)418 GNETLSEditorFrame::onCmdPhaseSwitch(FXObject*, FXSelector, void*) {
419 const int index = myTLSPhases->getPhaseTable()->getCurrentRow();
420 const NBTrafficLightLogic::PhaseDefinition& phase = getPhases()[index];
421 myTLSPhases->getPhaseTable()->selectRow(index);
422 // need not hold since links could have been deleted somewhere else and indices may be reused
423 // assert(phase.state.size() == myInternalLanes.size());
424 for (auto it : myInternalLanes) {
425 int tlIndex = it.first;
426 std::vector<GNEInternalLane*> lanes = it.second;
427 LinkState state = LINKSTATE_DEADEND;
428 if (tlIndex >= 0 && tlIndex < (int)phase.state.size()) {
429 state = (LinkState)phase.state[tlIndex];
430 }
431 for (auto it_lane : lanes) {
432 it_lane->setLinkState(state);
433 }
434 }
435 myViewNet->update();
436 return 1;
437 }
438
439 bool
fixedDuration() const440 GNETLSEditorFrame::fixedDuration() const {
441 assert(myEditedDef != nullptr);
442 return myEditedDef->getType() == TLTYPE_STATIC;
443 }
444
445 long
onCmdPhaseCreate(FXObject *,FXSelector,void *)446 GNETLSEditorFrame::onCmdPhaseCreate(FXObject*, FXSelector, void*) {
447 myTLSModifications->setHaveModifications(true);
448 // allows insertion at first position by deselecting via arrow keys
449 int newIndex = myTLSPhases->getPhaseTable()->getSelStartRow() + 1;
450 int oldIndex = MAX2(0, myTLSPhases->getPhaseTable()->getSelStartRow());
451 // copy current row
452 SUMOTime duration = getSUMOTime(myTLSPhases->getPhaseTable()->getItemText(oldIndex, 0));
453 std::string state = myTLSPhases->getPhaseTable()->getItemText(oldIndex, fixedDuration() ? 1 : 3).text();
454
455 std::set<int> crossingIndices;
456 for (NBNode* n : myEditedDef->getNodes()) {
457 for (NBNode::Crossing* c : n->getCrossings()) {
458 crossingIndices.insert(c->tlLinkIndex);
459 crossingIndices.insert(c->tlLinkIndex2);
460 }
461 }
462
463 // smart adapations for new state
464 bool haveGreen = false;
465 bool haveYellow = false;
466 for (char c : state) {
467 if (c == LINKSTATE_TL_GREEN_MAJOR || c == LINKSTATE_TL_GREEN_MINOR) {
468 haveGreen = true;
469 } else if (c == LINKSTATE_TL_YELLOW_MAJOR || c == LINKSTATE_TL_YELLOW_MINOR) {
470 haveYellow = true;
471 }
472 }
473 const OptionsCont& oc = OptionsCont::getOptions();
474 if (haveGreen && haveYellow) {
475 // guess left-mover state
476 duration = TIME2STEPS(oc.getInt("tls.left-green.time"));
477 for (int i = 0; i < (int)state.size(); i++) {
478 if (state[i] == LINKSTATE_TL_YELLOW_MAJOR || state[i] == LINKSTATE_TL_YELLOW_MINOR) {
479 state[i] = LINKSTATE_TL_RED;
480 } else if (state[i] == LINKSTATE_TL_GREEN_MINOR) {
481 state[i] = LINKSTATE_TL_GREEN_MAJOR;
482 }
483 }
484 } else if (haveGreen) {
485 // guess yellow state
486 myEditedDef->setParticipantsInformation();
487 duration = TIME2STEPS(myEditedDef->computeBrakingTime(oc.getFloat("tls.yellow.min-decel")));
488 for (int i = 0; i < (int)state.size(); i++) {
489 if (state[i] == LINKSTATE_TL_GREEN_MAJOR || state[i] == LINKSTATE_TL_GREEN_MINOR) {
490 if (crossingIndices.count(i) == 0) {
491 state[i] = LINKSTATE_TL_YELLOW_MINOR;
492 } else {
493 state[i] = LINKSTATE_TL_RED;
494 }
495 }
496 }
497 } else if (haveYellow) {
498 duration = TIME2STEPS(oc.isDefault("tls.allred.time") ? 2 : oc.getInt("tls.allred.time"));
499 // guess all-red state
500 for (int i = 0; i < (int)state.size(); i++) {
501 if (state[i] == LINKSTATE_TL_YELLOW_MAJOR || state[i] == LINKSTATE_TL_YELLOW_MINOR) {
502 state[i] = LINKSTATE_TL_RED;
503 }
504 }
505 }
506
507 myEditedDef->getLogic()->addStep(duration, state, std::vector<int>(), "", newIndex);
508 myTLSPhases->getPhaseTable()->setCurrentItem(newIndex, 0);
509 myTLSPhases->initPhaseTable(newIndex);
510 myTLSPhases->getPhaseTable()->setFocus();
511 return 1;
512 }
513
514
515 long
onCmdPhaseDelete(FXObject *,FXSelector,void *)516 GNETLSEditorFrame::onCmdPhaseDelete(FXObject*, FXSelector, void*) {
517 myTLSModifications->setHaveModifications(true);
518 const int newRow = MAX2((int)0, (int)myTLSPhases->getPhaseTable()->getCurrentRow() - 1);
519 myEditedDef->getLogic()->deletePhase(myTLSPhases->getPhaseTable()->getCurrentRow());
520 myTLSPhases->initPhaseTable(newRow);
521 myTLSPhases->getPhaseTable()->setFocus();
522 return 1;
523 }
524
525
526 long
onCmdCleanup(FXObject *,FXSelector,void *)527 GNETLSEditorFrame::onCmdCleanup(FXObject*, FXSelector, void*) {
528 myTLSModifications->setHaveModifications(myEditedDef->cleanupStates());
529 myTLSPhases->initPhaseTable(0);
530 myTLSPhases->getPhaseTable()->setFocus();
531 return 1;
532 }
533
534
535 long
onCmdAddUnused(FXObject *,FXSelector,void *)536 GNETLSEditorFrame::onCmdAddUnused(FXObject*, FXSelector, void*) {
537 myEditedDef->getLogic()->setStateLength(
538 myEditedDef->getLogic()->getNumLinks() + 1);
539 myTLSModifications->setHaveModifications(true);
540 myTLSPhases->initPhaseTable(0);
541 myTLSPhases->getPhaseTable()->setFocus();
542 return 1;
543 }
544
545
546 long
onCmdPhaseEdit(FXObject *,FXSelector,void * ptr)547 GNETLSEditorFrame::onCmdPhaseEdit(FXObject*, FXSelector, void* ptr) {
548 /* @note: there is a bug when copying/pasting rows: when this handler is
549 * called the value of the cell is not yet updated. This means you have to
550 * click inside the cell and hit enter to actually update the value */
551 FXTablePos* tp = (FXTablePos*)ptr;
552 FXString value = myTLSPhases->getPhaseTable()->getItemText(tp->row, tp->col);
553 const int colDuration = 0;
554 const int colMinDur = fixedDuration() ? -1 : 1;
555 const int colMaxDur = fixedDuration() ? -1 : 2;
556 const int colState = fixedDuration() ? 1 : 3;
557 const int colNext = fixedDuration() ? 2 : 4;
558 const int colName = fixedDuration() ? 3 : 5;
559
560 if (tp->col == colDuration) {
561 // duration edited
562 if (GNEAttributeCarrier::canParse<double>(value.text())) {
563 SUMOTime duration = getSUMOTime(value);
564 if (duration > 0) {
565 myEditedDef->getLogic()->setPhaseDuration(tp->row, duration);
566 myTLSModifications->setHaveModifications(true);
567 myTLSPhases->updateCycleDuration();
568 return 1;
569 }
570 }
571 // input error, reset value
572 myTLSPhases->getPhaseTable()->setItemText(tp->row, colDuration, toString(STEPS2TIME(getPhases()[tp->row].duration)).c_str());
573 } else if (tp->col == colMinDur) {
574 // minDur edited
575 if (GNEAttributeCarrier::canParse<double>(value.text())) {
576 SUMOTime minDur = getSUMOTime(value);
577 if (minDur > 0) {
578 myEditedDef->getLogic()->setPhaseMinDuration(tp->row, minDur);
579 myTLSModifications->setHaveModifications(true);
580 return 1;
581 }
582 } else if (StringUtils::prune(value.text()).empty()) {
583 myEditedDef->getLogic()->setPhaseMinDuration(tp->row, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
584 myTLSModifications->setHaveModifications(true);
585 return 1;
586 }
587 // input error, reset value
588 myTLSPhases->getPhaseTable()->setItemText(tp->row, colMinDur, varDurString(getPhases()[tp->row].minDur).c_str());
589 } else if (tp->col == colMaxDur) {
590 // maxDur edited
591 if (GNEAttributeCarrier::canParse<double>(value.text())) {
592 SUMOTime maxDur = getSUMOTime(value);
593 if (maxDur > 0) {
594 myEditedDef->getLogic()->setPhaseMaxDuration(tp->row, maxDur);
595 myTLSModifications->setHaveModifications(true);
596 return 1;
597 }
598 } else if (StringUtils::prune(value.text()).empty()) {
599 myEditedDef->getLogic()->setPhaseMaxDuration(tp->row, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
600 myTLSModifications->setHaveModifications(true);
601 return 1;
602 }
603 // input error, reset value
604 myTLSPhases->getPhaseTable()->setItemText(tp->row, colMaxDur, varDurString(getPhases()[tp->row].maxDur).c_str());
605 } else if (tp->col == colState) {
606 // state edited
607 try {
608 // insert phase with new step and delete the old phase
609 const NBTrafficLightLogic::PhaseDefinition& phase = getPhases()[tp->row];
610 myEditedDef->getLogic()->addStep(phase.duration, value.text(), phase.next, phase.name, tp->row);
611 myEditedDef->getLogic()->deletePhase(tp->row + 1);
612 myTLSModifications->setHaveModifications(true);
613 onCmdPhaseSwitch(nullptr, 0, nullptr);
614 } catch (ProcessError&) {
615 // input error, reset value
616 myTLSPhases->getPhaseTable()->setItemText(tp->row, colState, getPhases()[tp->row].state.c_str());
617 }
618 } else if (tp->col == colNext) {
619 // next edited
620 bool ok = true;
621 if (GNEAttributeCarrier::canParse<std::vector<int> >(value.text())) {
622 std::vector<int> next = GNEAttributeCarrier::parse<std::vector<int> >(value.text());
623 for (int n : next) {
624 if (n < 0 || n >= myTLSPhases->getPhaseTable()->getNumRows()) {
625 ok = false;
626 break;
627 }
628 }
629 if (ok) {
630 myEditedDef->getLogic()->setPhaseNext(tp->row, next);
631 myTLSModifications->setHaveModifications(true);
632 return 1;
633 }
634 }
635 // input error, reset value
636 myTLSPhases->getPhaseTable()->setItemText(tp->row, colNext, "");
637 } else if (tp->col == colName) {
638 // name edited
639 myEditedDef->getLogic()->setPhaseName(tp->row, value.text());
640 myTLSModifications->setHaveModifications(true);
641 return 1;
642 }
643 return 1;
644 }
645
646
647 void
cleanup()648 GNETLSEditorFrame::cleanup() {
649 if (myTLSJunction->getCurrentJunction()) {
650 myTLSJunction->getCurrentJunction()->selectTLS(false);
651 if (myTLSAttributes->getNumberOfTLSDefinitions() > 0) {
652 for (NBNode* node : myTLSAttributes->getCurrentTLSDefinition()->getNodes()) {
653 myViewNet->getNet()->retrieveJunction(node->getID())->selectTLS(false);
654 }
655 }
656 }
657 // clean data structures
658 myTLSJunction->setCurrentJunction(nullptr);
659 myTLSModifications->setHaveModifications(false);
660 delete myEditedDef;
661 myEditedDef = nullptr;
662 buildIinternalLanes(nullptr); // only clears
663 // clean up controls
664 myTLSAttributes->clearTLSAttributes();
665 myTLSPhases->initPhaseTable(); // only clears when there are no definitions
666 myTLSPhases->hideCycleDuration();
667 myTLSJunction->updateJunctionDescription();
668 }
669
670
671 void
buildIinternalLanes(NBTrafficLightDefinition * tlDef)672 GNETLSEditorFrame::buildIinternalLanes(NBTrafficLightDefinition* tlDef) {
673 SUMORTree& rtree = myViewNet->getNet()->getVisualisationSpeedUp();
674 // clean up previous objects
675 for (auto it : myInternalLanes) {
676 for (auto it_intLanes : it.second) {
677 rtree.removeAdditionalGLObject(it_intLanes);
678 delete it_intLanes;
679 }
680 }
681 myInternalLanes.clear();
682 // create new internal lanes
683 if (tlDef != nullptr) {
684 const int NUM_POINTS = 10;
685 assert(myTLSJunction->getCurrentJunction());
686 NBNode* nbn = myTLSJunction->getCurrentJunction()->getNBNode();
687 std::string innerID = ":" + nbn->getID(); // see NWWriter_SUMO::writeInternalEdges
688 const NBConnectionVector& links = tlDef->getControlledLinks();
689 for (auto it : links) {
690 int tlIndex = it.getTLIndex();
691 PositionVector shape = it.getFrom()->getToNode()->computeInternalLaneShape(it.getFrom(), NBEdge::Connection(it.getFromLane(),
692 it.getTo(), it.getToLane()), NUM_POINTS);
693 if (shape.length() < 2) {
694 // enlarge shape to ensure visibility
695 shape.clear();
696 PositionVector laneShapeFrom = it.getFrom()->getLaneShape(it.getFromLane());
697 PositionVector laneShapeTo = it.getTo()->getLaneShape(it.getToLane());
698 shape.push_back(laneShapeFrom.positionAtOffset(MAX2(0.0, laneShapeFrom.length() - 1)));
699 shape.push_back(laneShapeTo.positionAtOffset(MIN2(1.0, laneShapeFrom.length())));
700 }
701 GNEInternalLane* ilane = new GNEInternalLane(this, innerID + '_' + toString(tlIndex), shape, tlIndex);
702 rtree.addAdditionalGLObject(ilane);
703 myInternalLanes[tlIndex].push_back(ilane);
704 }
705 for (NBNode* nbn : tlDef->getNodes()) {
706 for (auto c : nbn->getCrossings()) {
707 if (c->tlLinkIndex2 > 0 && c->tlLinkIndex2 != c->tlLinkIndex) {
708 // draw both directions
709 PositionVector forward = c->shape;
710 forward.move2side(c->width / 4);
711 GNEInternalLane* ilane = new GNEInternalLane(this, c->id, forward, c->tlLinkIndex);
712 rtree.addAdditionalGLObject(ilane);
713 myInternalLanes[c->tlLinkIndex].push_back(ilane);
714
715 PositionVector backward = c->shape.reverse();
716 backward.move2side(c->width / 4);
717 GNEInternalLane* ilane2 = new GNEInternalLane(this, c->id + "_r", backward, c->tlLinkIndex2);
718 rtree.addAdditionalGLObject(ilane2);
719 myInternalLanes[c->tlLinkIndex2].push_back(ilane2);
720 } else {
721 // draw only one lane for both directions
722 GNEInternalLane* ilane = new GNEInternalLane(this, c->id, c->shape, c->tlLinkIndex);
723 rtree.addAdditionalGLObject(ilane);
724 myInternalLanes[c->tlLinkIndex].push_back(ilane);
725 }
726 }
727 }
728 }
729 }
730
731
732 std::string
varDurString(SUMOTime dur)733 GNETLSEditorFrame::varDurString(SUMOTime dur) {
734 return dur == NBTrafficLightDefinition::UNSPECIFIED_DURATION ? " " : toString(STEPS2TIME(dur));
735 }
736
737
738 const std::vector<NBTrafficLightLogic::PhaseDefinition>&
getPhases()739 GNETLSEditorFrame::getPhases() {
740 return myEditedDef->getLogic()->getPhases();
741 }
742
743
744 void
handleChange(GNEInternalLane * lane)745 GNETLSEditorFrame::handleChange(GNEInternalLane* lane) {
746 myTLSModifications->setHaveModifications(true);
747 if (myViewNet->changeAllPhases()) {
748 const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = getPhases();
749 for (int row = 0; row < (int)phases.size(); row++) {
750 myEditedDef->getLogic()->setPhaseState(row, lane->getTLIndex(), lane->getLinkState());
751 }
752 } else {
753 myEditedDef->getLogic()->setPhaseState(myTLSPhases->getPhaseTable()->getCurrentRow(), lane->getTLIndex(), lane->getLinkState());
754 }
755 myTLSPhases->initPhaseTable(myTLSPhases->getPhaseTable()->getCurrentRow());
756 myTLSPhases->getPhaseTable()->setFocus();
757 }
758
759
760 void
handleMultiChange(GNELane * lane,FXObject * obj,FXSelector sel,void * eventData)761 GNETLSEditorFrame::handleMultiChange(GNELane* lane, FXObject* obj, FXSelector sel, void* eventData) {
762 if (myEditedDef != nullptr) {
763 myTLSModifications->setHaveModifications(true);
764 const NBConnectionVector& links = myEditedDef->getControlledLinks();
765 std::set<std::string> fromIDs;
766 fromIDs.insert(lane->getMicrosimID());
767 GNEEdge& edge = lane->getParentEdge();
768 // if neither the lane nor its edge are selected, apply changes to the whole edge
769 if (!edge.isAttributeCarrierSelected() && !lane->isAttributeCarrierSelected()) {
770 for (auto it_lane : edge.getLanes()) {
771 fromIDs.insert(it_lane->getMicrosimID());
772 }
773 } else {
774 // if the edge is selected, apply changes to all lanes of all selected edges
775 if (edge.isAttributeCarrierSelected()) {
776 std::vector<GNEEdge*> edges = myViewNet->getNet()->retrieveEdges(true);
777 for (auto it : edges) {
778 for (auto it_lane : it->getLanes()) {
779 fromIDs.insert(it_lane->getMicrosimID());
780 }
781 }
782 }
783 // if the lane is selected, apply changes to all selected lanes
784 if (lane->isAttributeCarrierSelected()) {
785 std::vector<GNELane*> lanes = myViewNet->getNet()->retrieveLanes(true);
786 for (auto it_lane : lanes) {
787 fromIDs.insert(it_lane->getMicrosimID());
788 }
789 }
790
791 }
792 // set new state for all connections from the chosen lane IDs
793 for (auto it : links) {
794 if (fromIDs.count(it.getFrom()->getLaneID(it.getFromLane())) > 0) {
795 std::vector<GNEInternalLane*> lanes = myInternalLanes[it.getTLIndex()];
796 for (auto it_lane : lanes) {
797 it_lane->onDefault(obj, sel, eventData);
798 }
799 }
800 }
801 }
802 }
803
804
805 bool
controlsEdge(GNEEdge & edge) const806 GNETLSEditorFrame::controlsEdge(GNEEdge& edge) const {
807 if (myEditedDef != nullptr) {
808 const NBConnectionVector& links = myEditedDef->getControlledLinks();
809 for (auto it : links) {
810 if (it.getFrom()->getID() == edge.getMicrosimID()) {
811 return true;
812 }
813 }
814 }
815 return false;
816 }
817
818
819 SUMOTime
getSUMOTime(const FXString & string)820 GNETLSEditorFrame::getSUMOTime(const FXString& string) {
821 assert(GNEAttributeCarrier::canParse<double>(string.text()));
822 return TIME2STEPS(GNEAttributeCarrier::parse<double>(string.text()));
823 }
824
825 // ---------------------------------------------------------------------------
826 // GNETLSEditorFrame::TLSAttributes - methods
827 // ---------------------------------------------------------------------------
828
TLSAttributes(GNETLSEditorFrame * TLSEditorParent)829 GNETLSEditorFrame::TLSAttributes::TLSAttributes(GNETLSEditorFrame* TLSEditorParent) :
830 FXGroupBox(TLSEditorParent->myContentFrame, "Traffic light Attributes", GUIDesignGroupBoxFrame),
831 myTLSEditorParent(TLSEditorParent) {
832
833 // create frame, label and textfield for name (By default disabled)
834 FXHorizontalFrame* nameFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
835 myNameLabel = new FXLabel(nameFrame, "ID", nullptr, GUIDesignLabelAttribute);
836 myNameTextField = new FXTextField(nameFrame, GUIDesignTextFieldNCol, myTLSEditorParent, MID_GNE_TLSFRAME_SWITCH, GUIDesignTextField);
837 myNameTextField->disable();
838
839 // create frame, label and comboBox for Program (By default hidden)
840 FXHorizontalFrame* programFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
841 myProgramLabel = new FXLabel(programFrame, "Program", nullptr, GUIDesignLabelAttribute);
842 myProgramComboBox = new FXComboBox(programFrame, GUIDesignComboBoxNCol, myTLSEditorParent, MID_GNE_TLSFRAME_SWITCH, GUIDesignComboBoxAttribute);
843 myProgramComboBox->disable();
844
845 // create frame, label and TextField for Offset (By default disabled)
846 FXHorizontalFrame* offsetFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
847 myOffsetLabel = new FXLabel(offsetFrame, "Offset", nullptr, GUIDesignLabelAttribute);
848 myOffsetTextField = new FXTextField(offsetFrame, GUIDesignTextFieldNCol, myTLSEditorParent, MID_GNE_TLSFRAME_OFFSET, GUIDesignTextFieldReal);
849 myOffsetTextField->disable();
850 }
851
852
~TLSAttributes()853 GNETLSEditorFrame::TLSAttributes::~TLSAttributes() {}
854
855
856 void
initTLSAttributes(GNEJunction * junction)857 GNETLSEditorFrame::TLSAttributes::initTLSAttributes(GNEJunction* junction) {
858 assert(junction);
859 myTLSDefinitions.clear();
860 // enable name TextField
861 myNameTextField->enable();
862 // enable Offset
863 myOffsetTextField->enable();
864 // obtain TLSs
865 for (auto it : junction->getNBNode()->getControllingTLS()) {
866 myTLSDefinitions.push_back(it);
867 myNameTextField->setText(it->getID().c_str());
868 myNameTextField->enable();
869 myProgramComboBox->appendItem(it->getProgramID().c_str());
870 }
871 if (myTLSDefinitions.size() > 0) {
872 myProgramComboBox->enable();
873 myProgramComboBox->setCurrentItem(0);
874 myProgramComboBox->setNumVisible(myProgramComboBox->getNumItems());
875 myTLSEditorParent->onCmdDefSwitch(nullptr, 0, nullptr);
876 }
877 }
878
879
880 void
clearTLSAttributes()881 GNETLSEditorFrame::TLSAttributes::clearTLSAttributes() {
882 // clear definitions
883 myTLSDefinitions.clear();
884 // clear and disable name TextField
885 myNameTextField->setText("");
886 myNameTextField->disable();
887 // clear and disable myProgramComboBox
888 myProgramComboBox->clearItems();
889 myProgramComboBox->disable();
890 // clear and disable Offset TextField
891 myOffsetTextField->setText("");
892 myOffsetTextField->disable();
893 }
894
895
896 NBTrafficLightDefinition*
getCurrentTLSDefinition() const897 GNETLSEditorFrame::TLSAttributes::getCurrentTLSDefinition() const {
898 return myTLSDefinitions.at(myProgramComboBox->getCurrentItem());
899 }
900
901
902 int
getNumberOfTLSDefinitions() const903 GNETLSEditorFrame::TLSAttributes::getNumberOfTLSDefinitions() const {
904 return (int)myTLSDefinitions.size();
905 }
906
907
908 int
getNumberOfPrograms() const909 GNETLSEditorFrame::TLSAttributes::getNumberOfPrograms() const {
910 return myProgramComboBox->getNumItems();
911 }
912
913
914 SUMOTime
getOffset() const915 GNETLSEditorFrame::TLSAttributes::getOffset() const {
916 return getSUMOTime(myOffsetTextField->getText());
917 }
918
919
920 void
setOffset(SUMOTime offset)921 GNETLSEditorFrame::TLSAttributes::setOffset(SUMOTime offset) {
922 myOffsetTextField->setText(toString(STEPS2TIME(offset)).c_str());
923 }
924
925 // ---------------------------------------------------------------------------
926 // GNETLSEditorFrame::TLSJunction - methods
927 // ---------------------------------------------------------------------------
928
TLSJunction(GNETLSEditorFrame * TLSEditorParent)929 GNETLSEditorFrame::TLSJunction::TLSJunction(GNETLSEditorFrame* TLSEditorParent) :
930 FXGroupBox(TLSEditorParent->myContentFrame, "Junction", GUIDesignGroupBoxFrame),
931 myTLSEditorParent(TLSEditorParent),
932 myCurrentJunction(nullptr) {
933 // Create frame for junction ID
934 FXHorizontalFrame* junctionIDFrame = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
935 myLabelJunctionID = new FXLabel(junctionIDFrame, "Junction ID", nullptr, GUIDesignLabelAttribute);
936 myTextFieldJunctionID = new FXTextField(junctionIDFrame, GUIDesignTextFieldNCol, this, MID_GNE_TLSFRAME_SELECT_JUNCTION, GUIDesignTextField);
937 myTextFieldJunctionID->setEditable(false);
938 // create frame for junction status
939 FXHorizontalFrame* junctionIDStatus = new FXHorizontalFrame(this, GUIDesignAuxiliarHorizontalFrame);
940 myLabelJunctionStatus = new FXLabel(junctionIDStatus, "Status", nullptr, GUIDesignLabelAttribute);
941 myTextFieldJunctionStatus = new FXTextField(junctionIDStatus, GUIDesignTextFieldNCol, this, MID_GNE_TLSFRAME_UPDATE_STATUS, GUIDesignTextField);
942 myTextFieldJunctionStatus->setEditable(false);
943 // update junction description after creation
944 updateJunctionDescription();
945 // show TLS Junction
946 show();
947 }
948
949
~TLSJunction()950 GNETLSEditorFrame::TLSJunction::~TLSJunction() {}
951
952
953 GNEJunction*
getCurrentJunction() const954 GNETLSEditorFrame::TLSJunction::getCurrentJunction() const {
955 return myCurrentJunction;
956 }
957
958
959 void
setCurrentJunction(GNEJunction * junction)960 GNETLSEditorFrame::TLSJunction::setCurrentJunction(GNEJunction* junction) {
961 myCurrentJunction = junction;
962 }
963
964
965 void
updateJunctionDescription() const966 GNETLSEditorFrame::TLSJunction::updateJunctionDescription() const {
967 if (myCurrentJunction == nullptr) {
968 myTextFieldJunctionID->setText("");
969 myTextFieldJunctionStatus->setText("");
970 } else {
971 NBNode* nbn = myCurrentJunction->getNBNode();
972 myTextFieldJunctionID->setText(nbn->getID().c_str());
973 if (!nbn->isTLControlled()) {
974 myTextFieldJunctionStatus->setText("uncontrolled");
975 } else {
976 myTextFieldJunctionStatus->setText(myTLSEditorParent->myTLSModifications->checkHaveModifications() ? "modified" : "unmodified");
977 }
978 }
979 }
980
981 // ---------------------------------------------------------------------------
982 // GNETLSEditorFrame::TLSDefinition - methods
983 // ---------------------------------------------------------------------------
984
TLSDefinition(GNETLSEditorFrame * TLSEditorParent)985 GNETLSEditorFrame::TLSDefinition::TLSDefinition(GNETLSEditorFrame* TLSEditorParent) :
986 FXGroupBox(TLSEditorParent->myContentFrame, "Traffic lights definition", GUIDesignGroupBoxFrame),
987 myTLSEditorParent(TLSEditorParent) {
988 // create create tlDef button
989 myNewTLProgram = new FXButton(this, "Create TLS\t\tCreate a new traffic light program",
990 GUIIconSubSys::getIcon(ICON_MODETLS), TLSEditorParent, MID_GNE_TLSFRAME_CREATE, GUIDesignButton);
991 // create delete tlDef button
992 myDeleteTLProgram = new FXButton(this, "Delete TLS\t\tDelete a traffic light program. If all programs are deleted the junction turns into a priority junction.",
993 GUIIconSubSys::getIcon(ICON_REMOVE), TLSEditorParent, MID_GNE_TLSFRAME_DELETE, GUIDesignButton);
994 // show TLS TLSDefinition
995 show();
996 }
997
998
~TLSDefinition()999 GNETLSEditorFrame::TLSDefinition::~TLSDefinition() {}
1000
1001 // ---------------------------------------------------------------------------
1002 // GNETLSEditorFrame::TLSPhases - methods
1003 // ---------------------------------------------------------------------------
1004
TLSPhases(GNETLSEditorFrame * TLSEditorParent)1005 GNETLSEditorFrame::TLSPhases::TLSPhases(GNETLSEditorFrame* TLSEditorParent) :
1006 FXGroupBox(TLSEditorParent->myContentFrame, "Phases", GUIDesignGroupBoxFrame),
1007 myTLSEditorParent(TLSEditorParent),
1008 myTableFont(new FXFont(getApp(), "Courier New", 9)) {
1009
1010 // create and configure phase table
1011 myTableScroll = new FXScrollWindow(this, LAYOUT_FILL_X | LAYOUT_FIX_HEIGHT);
1012 myPhaseTable = new FXTable(myTableScroll, myTLSEditorParent, MID_GNE_TLSFRAME_PHASE_TABLE, GUIDesignTableLimitedHeight);
1013 myPhaseTable->setColumnHeaderMode(LAYOUT_FIX_HEIGHT);
1014 myPhaseTable->setColumnHeaderHeight(0);
1015 myPhaseTable->setRowHeaderMode(LAYOUT_FIX_WIDTH);
1016 myPhaseTable->setRowHeaderWidth(0);
1017 myPhaseTable->hide();
1018 myPhaseTable->setFont(myTableFont);
1019 myPhaseTable->setHelpText("phase duration in seconds | phase state");
1020
1021 // create total duration info label
1022 myCycleDuration = new FXLabel(this, "", nullptr, GUIDesignLabelLeft);
1023
1024 // create new phase button
1025 myInsertDuplicateButton = new FXButton(this, "Insert Phase\t\tInsert new phase after the selected phase. The new state is deduced from the selected phase.", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_PHASE_CREATE, GUIDesignButton);
1026
1027 // create delete phase button
1028 myDeleteSelectedPhaseButton = new FXButton(this, "Delete Phase\t\tDelete selected phase", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_PHASE_DELETE, GUIDesignButton);
1029
1030 // create cleanup states button
1031 new FXButton(this, "Cleanup States\t\tClean unused states from all phase.", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_CLEANUP, GUIDesignButton);
1032
1033 // add unused states button
1034 new FXButton(this, "Add Unused States\t\tExtend the state vector for all phases by one entry.", nullptr, myTLSEditorParent, MID_GNE_TLSFRAME_ADDUNUSED, GUIDesignButton);
1035
1036 // show TLSFile
1037 show();
1038 }
1039
1040
~TLSPhases()1041 GNETLSEditorFrame::TLSPhases::~TLSPhases() {
1042 delete myTableFont;
1043 }
1044
1045
1046 FXTable*
getPhaseTable() const1047 GNETLSEditorFrame::TLSPhases::getPhaseTable() const {
1048 return myPhaseTable;
1049 }
1050
1051
1052 void
initPhaseTable(int index)1053 GNETLSEditorFrame::TLSPhases::initPhaseTable(int index) {
1054 myPhaseTable->setVisibleRows(1);
1055 myPhaseTable->setVisibleColumns(2);
1056 myPhaseTable->hide();
1057 if (myTLSEditorParent->myTLSAttributes->getNumberOfTLSDefinitions() > 0) {
1058 const bool fixed = myTLSEditorParent->fixedDuration();
1059 const int cols = fixed ? 4 : 6;
1060 const int colDuration = 0;
1061 const int colMinDur = fixed ? -1 : 1;
1062 const int colMaxDur = fixed ? -1 : 2;
1063 const int colState = fixed ? 1 : 3;
1064 const int colNext = fixed ? 2 : 4;
1065 const int colName = fixed ? 3 : 5;
1066
1067 const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = myTLSEditorParent->getPhases();
1068 myPhaseTable->setTableSize((int)phases.size(), cols);
1069 myPhaseTable->setVisibleRows((int)phases.size());
1070 myPhaseTable->setVisibleColumns(cols);
1071 for (int row = 0; row < (int)phases.size(); row++) {
1072 myPhaseTable->setItemText(row, colDuration, toString(STEPS2TIME(phases[row].duration)).c_str());
1073 if (!fixed) {
1074 myPhaseTable->setItemText(row, colMinDur, varDurString(phases[row].minDur).c_str());
1075 myPhaseTable->setItemText(row, colMaxDur, varDurString(phases[row].maxDur).c_str());
1076 }
1077 myPhaseTable->setItemText(row, colState, phases[row].state.c_str());
1078 myPhaseTable->setItemText(row, colNext, phases[row].next.size() > 0 ? toString(phases[row].next).c_str() : " ");
1079 myPhaseTable->setItemText(row, colName, phases[row].name.c_str());
1080 myPhaseTable->getItem(row, 1)->setJustify(FXTableItem::LEFT);
1081 }
1082 myPhaseTable->fitColumnsToContents(0, cols);
1083 myPhaseTable->setHeight((int)phases.size() * 21); // experimental
1084 myPhaseTable->setCurrentItem(index, 0);
1085 myPhaseTable->selectRow(index, true);
1086 myPhaseTable->show();
1087 myPhaseTable->setFocus();
1088 myTableScroll->setHeight(myPhaseTable->getHeight() + 10);
1089 }
1090 update();
1091 }
1092
1093
1094 void
showCycleDuration()1095 GNETLSEditorFrame::TLSPhases::showCycleDuration() {
1096 myCycleDuration->show();
1097 }
1098
1099
1100 void
hideCycleDuration()1101 GNETLSEditorFrame::TLSPhases::hideCycleDuration() {
1102 myCycleDuration->hide();
1103 }
1104
1105 void
updateCycleDuration()1106 GNETLSEditorFrame::TLSPhases::updateCycleDuration() {
1107 SUMOTime cycleDuration = 0;
1108 for (auto it : myTLSEditorParent->getPhases()) {
1109 cycleDuration += it.duration;
1110 }
1111 std::string text = "Cycle time: " + toString(STEPS2TIME(cycleDuration));
1112 myCycleDuration->setText(text.c_str());
1113 }
1114
1115 // ---------------------------------------------------------------------------
1116 // GNETLSEditorFrame::TLSModifications - methods
1117 // ---------------------------------------------------------------------------
1118
TLSModifications(GNETLSEditorFrame * TLSEditorParent)1119 GNETLSEditorFrame::TLSModifications::TLSModifications(GNETLSEditorFrame* TLSEditorParent) :
1120 FXGroupBox(TLSEditorParent->myContentFrame, "Modifications", GUIDesignGroupBoxFrame),
1121 myTLSEditorParent(TLSEditorParent),
1122 myHaveModifications(false) {
1123 // create save modifications button
1124 mySaveModificationsButtons = new FXButton(this, "Save\t\tSave program modifications (Enter)",
1125 GUIIconSubSys::getIcon(ICON_OK), myTLSEditorParent, MID_OK, GUIDesignButton);
1126 // create discard modifications buttons
1127 myDiscardModificationsButtons = new FXButton(this, "Cancel\t\tDiscard program modifications (Esc)",
1128 GUIIconSubSys::getIcon(ICON_CANCEL), myTLSEditorParent, MID_CANCEL, GUIDesignButton);
1129 // show TLSModifications
1130 show();
1131 }
1132
1133
~TLSModifications()1134 GNETLSEditorFrame::TLSModifications::~TLSModifications() {}
1135
1136
1137 bool
checkHaveModifications() const1138 GNETLSEditorFrame::TLSModifications::checkHaveModifications() const {
1139 return myHaveModifications;
1140 }
1141
1142
1143 void
setHaveModifications(bool value)1144 GNETLSEditorFrame::TLSModifications::setHaveModifications(bool value) {
1145 myHaveModifications = value;
1146 }
1147
1148 // ---------------------------------------------------------------------------
1149 // GNETLSEditorFrame::TLSFile - methods
1150 // ---------------------------------------------------------------------------
1151
TLSFile(GNETLSEditorFrame * TLSEditorParent)1152 GNETLSEditorFrame::TLSFile::TLSFile(GNETLSEditorFrame* TLSEditorParent) :
1153 FXGroupBox(TLSEditorParent->myContentFrame, "TLS Program", GUIDesignGroupBoxFrame),
1154 myTLSEditorParent(TLSEditorParent) {
1155 // create create tlDef button
1156 myLoadTLSProgramButton = new FXButton(this, "Load TLS Program", nullptr, this, MID_GNE_TLSFRAME_LOAD_PROGRAM, GUIDesignButton);
1157 // create create tlDef button
1158 mySaveTLSProgramButton = new FXButton(this, "Save TLS Program", nullptr, this, MID_GNE_TLSFRAME_SAVE_PROGRAM, GUIDesignButton);
1159 // show TLSFile
1160 show();
1161 }
1162
1163
~TLSFile()1164 GNETLSEditorFrame::TLSFile::~TLSFile() {}
1165
1166
1167 long
onCmdLoadTLSProgram(FXObject *,FXSelector,void *)1168 GNETLSEditorFrame::TLSFile::onCmdLoadTLSProgram(FXObject*, FXSelector, void*) {
1169 FXFileDialog opendialog(this, "Load TLS Program");
1170 opendialog.setIcon(GUIIconSubSys::getIcon(ICON_MODETLS));
1171 opendialog.setSelectMode(SELECTFILE_EXISTING);
1172 opendialog.setPatternList("*.xml");
1173 if (gCurrentFolder.length() != 0) {
1174 opendialog.setDirectory(gCurrentFolder);
1175 }
1176 if (opendialog.execute()) {
1177 // run parser
1178 NBTrafficLightLogicCont tmpTLLCont;;
1179 NIXMLTrafficLightsHandler tllHandler(tmpTLLCont, myTLSEditorParent->myViewNet->getNet()->getEdgeCont(), true);
1180 tmpTLLCont.insert(myTLSEditorParent->myEditedDef);
1181 XMLSubSys::runParser(tllHandler, opendialog.getFilename().text());
1182
1183 NBLoadedSUMOTLDef* newDefSameProgram = nullptr;
1184 std::set<NBLoadedSUMOTLDef*> newDefsOtherProgram;
1185 for (auto item : tmpTLLCont.getPrograms(myTLSEditorParent->myEditedDef->getID())) {
1186 if (item.second != myTLSEditorParent->myEditedDef) {
1187 NBLoadedSUMOTLDef* sdef = dynamic_cast<NBLoadedSUMOTLDef*>(item.second);
1188 if (item.first == myTLSEditorParent->myEditedDef->getProgramID()) {
1189 newDefSameProgram = sdef;
1190 } else {
1191 newDefsOtherProgram.insert(sdef);
1192 }
1193 }
1194 }
1195 const int newPrograms = (int)newDefsOtherProgram.size();
1196 if (newPrograms > 0 || newDefSameProgram != nullptr) {
1197 std::vector<NBNode*> nodes = myTLSEditorParent->myEditedDef->getNodes();
1198 for (auto newProg : newDefsOtherProgram) {
1199 for (auto it_node : nodes) {
1200 GNEJunction* junction = myTLSEditorParent->getViewNet()->getNet()->retrieveJunction(it_node->getID());
1201 myTLSEditorParent->getViewNet()->getUndoList()->add(new GNEChange_TLS(junction, newProg, true), true);
1202 }
1203 }
1204 if (newPrograms > 0) {
1205 WRITE_MESSAGE("Loaded " + toString(newPrograms) + " new programs for tlLogic '" + myTLSEditorParent->myEditedDef->getID() + "'");
1206 }
1207 if (newDefSameProgram != nullptr) {
1208 // replace old program when loading the same program ID
1209 myTLSEditorParent->myEditedDef = newDefSameProgram;
1210 WRITE_MESSAGE("Updated program '" + newDefSameProgram->getProgramID() + "' for tlLogic '" + myTLSEditorParent->myEditedDef->getID() + "'");
1211 }
1212 } else {
1213 myTLSEditorParent->getViewNet()->setStatusBarText("No programs found for traffic light '" + myTLSEditorParent->myEditedDef->getID() + "'");
1214 }
1215
1216 // clean up temporary container to avoid deletion of defs when it's destruct is called
1217 for (NBTrafficLightDefinition* def : tmpTLLCont.getDefinitions()) {
1218 tmpTLLCont.removeProgram(def->getID(), def->getProgramID(), false);
1219 }
1220
1221 myTLSEditorParent->myTLSPhases->initPhaseTable();
1222 myTLSEditorParent->myTLSModifications->setHaveModifications(true);
1223 }
1224 return 0;
1225 }
1226
1227
1228 long
onCmdSaveTLSProgram(FXObject *,FXSelector,void *)1229 GNETLSEditorFrame::TLSFile::onCmdSaveTLSProgram(FXObject*, FXSelector, void*) {
1230 FXString file = MFXUtils::getFilename2Write(this,
1231 "Save TLS Program as", ".xml",
1232 GUIIconSubSys::getIcon(ICON_MODETLS),
1233 gCurrentFolder);
1234 if (file == "") {
1235 return 1;
1236 }
1237 OutputDevice& device = OutputDevice::getDevice(file.text());
1238
1239 // save program
1240 device.writeXMLHeader("additional", "additional_file.xsd");
1241 device.openTag(SUMO_TAG_TLLOGIC);
1242 device.writeAttr(SUMO_ATTR_ID, myTLSEditorParent->myEditedDef->getLogic()->getID());
1243 device.writeAttr(SUMO_ATTR_TYPE, myTLSEditorParent->myEditedDef->getLogic()->getType());
1244 device.writeAttr(SUMO_ATTR_PROGRAMID, myTLSEditorParent->myEditedDef->getLogic()->getProgramID());
1245 device.writeAttr(SUMO_ATTR_OFFSET, writeSUMOTime(myTLSEditorParent->myEditedDef->getLogic()->getOffset()));
1246 // write the phases
1247 const bool varPhaseLength = myTLSEditorParent->myEditedDef->getLogic()->getType() != TLTYPE_STATIC;
1248 const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = myTLSEditorParent->myEditedDef->getLogic()->getPhases();
1249 for (auto j : phases) {
1250 device.openTag(SUMO_TAG_PHASE);
1251 device.writeAttr(SUMO_ATTR_DURATION, writeSUMOTime(j.duration));
1252 device.writeAttr(SUMO_ATTR_STATE, j.state);
1253 if (varPhaseLength) {
1254 if (j.minDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1255 device.writeAttr(SUMO_ATTR_MINDURATION, writeSUMOTime(j.minDur));
1256 }
1257 if (j.maxDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1258 device.writeAttr(SUMO_ATTR_MAXDURATION, writeSUMOTime(j.maxDur));
1259 }
1260 }
1261 device.closeTag();
1262 }
1263 device.close();
1264 return 1;
1265 }
1266
1267
1268 std::string
writeSUMOTime(SUMOTime steps)1269 GNETLSEditorFrame::TLSFile::writeSUMOTime(SUMOTime steps) {
1270 double time = STEPS2TIME(steps);
1271 if (time == std::floor(time)) {
1272 return toString(int(time));
1273 } else {
1274 return toString(time);
1275 }
1276 }
1277
1278 long
onUpdNeedsDef(FXObject * o,FXSelector,void *)1279 GNETLSEditorFrame::TLSFile::onUpdNeedsDef(FXObject* o, FXSelector, void*) {
1280 const bool enable = myTLSEditorParent->myTLSAttributes->getNumberOfTLSDefinitions() > 0;
1281 o->handle(this, FXSEL(SEL_COMMAND, enable ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE), nullptr);
1282 return 1;
1283 }
1284 /****************************************************************************/
1285