1 package org.opentrafficsim.trafficcontrol.trafcod;
2
3 import java.awt.Container;
4 import java.awt.geom.Point2D;
5 import java.awt.image.BufferedImage;
6 import java.io.BufferedReader;
7 import java.io.IOException;
8 import java.io.InputStreamReader;
9 import java.io.Serializable;
10 import java.net.URL;
11 import java.rmi.RemoteException;
12 import java.util.ArrayList;
13 import java.util.EnumSet;
14 import java.util.LinkedHashMap;
15 import java.util.LinkedHashSet;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.Set;
20
21 import org.djunits.unit.DurationUnit;
22 import org.djunits.value.vdouble.scalar.Duration;
23 import org.djunits.value.vdouble.scalar.Time;
24 import org.djutils.event.EventInterface;
25 import org.djutils.event.EventListenerInterface;
26 import org.djutils.event.EventType;
27 import org.djutils.exceptions.Throw;
28 import org.djutils.immutablecollections.ImmutableCollection;
29 import org.opentrafficsim.core.dsol.OTSModelInterface;
30 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
31 import org.opentrafficsim.core.network.Network;
32 import org.opentrafficsim.core.network.NetworkException;
33 import org.opentrafficsim.core.network.OTSNetwork;
34 import org.opentrafficsim.core.object.InvisibleObjectInterface;
35 import org.opentrafficsim.core.object.ObjectInterface;
36 import org.opentrafficsim.road.network.lane.object.sensor.NonDirectionalOccupancySensor;
37 import org.opentrafficsim.road.network.lane.object.sensor.TrafficLightSensor;
38 import org.opentrafficsim.road.network.lane.object.trafficlight.FlankSensor;
39 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
40 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
41 import org.opentrafficsim.trafficcontrol.AbstractTrafficController;
42 import org.opentrafficsim.trafficcontrol.ActuatedTrafficController;
43 import org.opentrafficsim.trafficcontrol.TrafficControlException;
44 import org.opentrafficsim.trafficcontrol.TrafficController;
45
46 import nl.tudelft.simulation.dsol.SimRuntimeException;
47 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
48 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
49
50
51
52
53
54
55
56
57
58
59
60 public class TrafCOD extends AbstractTrafficController implements ActuatedTrafficController, EventListenerInterface
61 {
62
63 private static final long serialVersionUID = 20161014L;
64
65
66 final static int TRAFCOD_VERSION = 100;
67
68
69 final static Duration EVALUATION_INTERVAL = new Duration(0.1, DurationUnit.SECOND);
70
71
72 private final static String VERSION_PREFIX = "trafcod-version=";
73
74
75 private final static String SEQUENCE_KEY = "Sequence";
76
77
78 private final static String STRUCTURE_PREFIX = "Structure:";
79
80
81 final List<Object[]> tokenisedRules = new ArrayList<>();
82
83
84 final Map<String, Variable> variables = new LinkedHashMap<>();
85
86
87 final List<Variable> variablesInDefinitionOrder = new ArrayList<>();
88
89
90 final Map<String, Variable> detectors = new LinkedHashMap<>();
91
92
93 final static String COMMENT_PREFIX = "#";
94
95
96 private final static String INIT_PREFIX = "%init ";
97
98
99 private final static String TIME_PREFIX = "%time ";
100
101
102 private final static String EXPORT_PREFIX = "%export ";
103
104
105 int numberOfConflictGroups = -1;
106
107
108 private int conflictGroupSize = -1;
109
110
111 private int structureNumber = -1;
112
113
114 private List<List<Short>> conflictGroups = new ArrayList<List<Short>>();
115
116
117 private int maxLoopCount = 10;
118
119
120 private int currentToken;
121
122
123 private List<Integer> stack = new ArrayList<Integer>();
124
125
126 private Object[] currentRule;
127
128
129 private int currentTime10 = 0;
130
131
132 private final List<String> trafCODRules;
133
134
135 private final Container displayContainer;
136
137
138 final BufferedImage displayBackground;
139
140
141 final List<String> displayObjectLocations;
142
143
144 private TrafCODDisplay stateDisplay = null;
145
146
147 private final OTSSimulatorInterface simulator;
148
149
150 private String currentConflictGroup = "";
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165 public TrafCOD(String controllerName, final URL trafCodURL, final OTSSimulatorInterface simulator, Container display,
166 final BufferedImage displayBackground, final List<String> displayObjectLocations)
167 throws TrafficControlException, SimRuntimeException, IOException
168 {
169 this(controllerName, loadTextFromURL(trafCodURL), simulator, display, displayBackground, displayObjectLocations);
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183
184 public TrafCOD(String controllerName, final List<String> trafCODRules, final OTSSimulatorInterface simulator,
185 final Container display, final BufferedImage displayBackground, final List<String> displayObjectLocations)
186 throws TrafficControlException, SimRuntimeException
187 {
188 super(controllerName, simulator);
189 Throw.whenNull(controllerName, "controllerName may not be null");
190 Throw.whenNull(simulator, "simulator may not be null");
191 this.simulator = simulator;
192 this.displayContainer = display;
193 this.displayBackground = displayBackground;
194 this.displayObjectLocations = displayObjectLocations;
195 Throw.whenNull(trafCODRules, "trafCodRules may not be null");
196 this.trafCODRules = trafCODRules;
197 parseTrafCODRules();
198
199
200 for (Variable v : this.variablesInDefinitionOrder)
201 {
202 v.initialize();
203 double value = v.getValue();
204 if (v.isTimer())
205 {
206 value /= 10.0;
207 }
208 fireTimedEvent(TrafficController.TRAFFICCONTROL_VARIABLE_CREATED,
209 new Object[] {getId(), v.getName(), v.getStream(), value}, simulator.getSimulatorTime());
210 }
211 if (null != this.displayContainer && null != this.displayBackground && null != this.displayObjectLocations)
212 {
213 this.stateDisplay = new TrafCODDisplay(this.displayBackground);
214 this.displayContainer.add(this.stateDisplay);
215 try
216 {
217 addTrafCODDisplay(this.displayObjectLocations);
218 }
219 catch (IOException e)
220 {
221 e.printStackTrace();
222 }
223 }
224
225
226 this.simulator.scheduleEventRel(Duration.ZERO, this, this, "checkConsistency", null);
227
228 this.simulator.scheduleEventRel(EVALUATION_INTERVAL, this, this, "evalExprs", null);
229 }
230
231
232
233
234
235
236
237 public static List<String> loadTextFromURL(final URL url) throws IOException
238 {
239 List<String> result = new ArrayList<>();
240 BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
241 String inputLine;
242 while ((inputLine = in.readLine()) != null)
243 {
244 result.add(inputLine.trim());
245 }
246 return result;
247 }
248
249
250
251
252
253 private void parseTrafCODRules() throws TrafficControlException
254 {
255 for (int lineno = 0; lineno < this.trafCODRules.size(); lineno++)
256 {
257 String trimmedLine = this.trafCODRules.get(lineno);
258
259 if (trimmedLine.length() == 0)
260 {
261 continue;
262 }
263 String locationDescription = "TrafCOD rule" + "(" + lineno + ") ";
264 if (trimmedLine.startsWith(COMMENT_PREFIX))
265 {
266 String commentStripped = trimmedLine.substring(1).trim();
267 if (stringBeginsWithIgnoreCase(VERSION_PREFIX, commentStripped))
268 {
269 String versionString = commentStripped.substring(VERSION_PREFIX.length());
270 try
271 {
272 int observedVersion = Integer.parseInt(versionString);
273 if (TRAFCOD_VERSION != observedVersion)
274 {
275 throw new TrafficControlException(
276 "Wrong TrafCOD version (expected " + TRAFCOD_VERSION + ", got " + observedVersion + ")");
277 }
278 }
279 catch (NumberFormatException nfe)
280 {
281 nfe.printStackTrace();
282 throw new TrafficControlException("Could not parse TrafCOD version (got \"" + versionString + ")");
283 }
284 }
285 else if (stringBeginsWithIgnoreCase(SEQUENCE_KEY, commentStripped))
286 {
287 while (trimmedLine.startsWith(COMMENT_PREFIX))
288 {
289 if (++lineno >= this.trafCODRules.size())
290 {
291 throw new TrafficControlException(
292 "Unexpected EOF (reading sequence key at " + locationDescription + ")");
293 }
294 trimmedLine = this.trafCODRules.get(lineno);
295 }
296 String[] fields = trimmedLine.split("\\s");
297 Throw.when(fields.length != 2, TrafficControlException.class,
298 "Wrong number of fields in Sequence information line (%s)", trimmedLine);
299 try
300 {
301 this.numberOfConflictGroups = Integer.parseInt(fields[0]);
302 this.conflictGroupSize = Integer.parseInt(fields[1]);
303 }
304 catch (NumberFormatException nfe)
305 {
306 nfe.printStackTrace();
307 throw new TrafficControlException("Bad number of conflict groups or bad conflict group size");
308 }
309 }
310 else if (stringBeginsWithIgnoreCase(STRUCTURE_PREFIX, commentStripped))
311 {
312 String structureNumberString = commentStripped.substring(STRUCTURE_PREFIX.length()).trim();
313 try
314 {
315 this.structureNumber = Integer.parseInt(structureNumberString);
316 }
317 catch (NumberFormatException nfe)
318 {
319 nfe.printStackTrace();
320 throw new TrafficControlException(
321 "Bad structure number (got \"" + structureNumberString + "\" at " + locationDescription + ")");
322 }
323 for (int i = 0; i < this.conflictGroupSize; i++)
324 {
325 this.conflictGroups.add(new ArrayList<Short>());
326 }
327 for (int conflictMemberLine = 0; conflictMemberLine < this.numberOfConflictGroups; conflictMemberLine++)
328 {
329 if (++lineno >= this.trafCODRules.size())
330 {
331 throw new TrafficControlException(
332 "Unexpected EOF (reading conflict groups at " + locationDescription + ")");
333 }
334 trimmedLine = this.trafCODRules.get(lineno);
335 while (trimmedLine.startsWith(COMMENT_PREFIX))
336 {
337 if (++lineno >= this.trafCODRules.size())
338 {
339 throw new TrafficControlException(
340 "Unexpected EOF (reading conflict groups at " + locationDescription + ")");
341 }
342 trimmedLine = this.trafCODRules.get(lineno);
343 }
344 String[] fields = trimmedLine.split("\\s+");
345 if (fields.length != this.conflictGroupSize)
346 {
347 throw new TrafficControlException("Wrong number of conflict groups in Structure information");
348 }
349 for (int col = 0; col < this.conflictGroupSize; col++)
350 {
351 try
352 {
353 Short stream = Short.parseShort(fields[col]);
354 this.conflictGroups.get(col).add(stream);
355 }
356 catch (NumberFormatException nfe)
357 {
358 nfe.printStackTrace();
359 throw new TrafficControlException("Wrong number of streams in conflict group " + trimmedLine);
360 }
361 }
362 }
363 }
364 continue;
365 }
366 if (stringBeginsWithIgnoreCase(INIT_PREFIX, trimmedLine))
367 {
368 String varNameAndInitialValue = trimmedLine.substring(INIT_PREFIX.length()).trim().replaceAll("[ \t]+", " ");
369 String[] fields = varNameAndInitialValue.split(" ");
370 NameAndStreamTrafCOD.html#NameAndStream">NameAndStream nameAndStream = new NameAndStream(fields[0], locationDescription);
371 installVariable(nameAndStream.getName(), nameAndStream.getStream(), EnumSet.noneOf(Flags.class),
372 locationDescription).setFlag(Flags.INITED);
373
374 continue;
375 }
376 if (stringBeginsWithIgnoreCase(TIME_PREFIX, trimmedLine))
377 {
378 String timerNameAndMaximumValue = trimmedLine.substring(INIT_PREFIX.length()).trim().replaceAll("[ \t]+", " ");
379 String[] fields = timerNameAndMaximumValue.split(" ");
380 NameAndStreamTrafCOD.html#NameAndStream">NameAndStream nameAndStream = new NameAndStream(fields[0], locationDescription);
381 Variable variable = installVariable(nameAndStream.getName(), nameAndStream.getStream(),
382 EnumSet.noneOf(Flags.class), locationDescription);
383 int value10 = Integer.parseInt(fields[1]);
384 variable.setTimerMax(value10);
385 continue;
386 }
387 if (stringBeginsWithIgnoreCase(EXPORT_PREFIX, trimmedLine))
388 {
389 String varNameAndOutputValue = trimmedLine.substring(EXPORT_PREFIX.length()).trim().replaceAll("[ \t]+", " ");
390 String[] fields = varNameAndOutputValue.split(" ");
391 NameAndStreamTrafCOD.html#NameAndStream">NameAndStream nameAndStream = new NameAndStream(fields[0], locationDescription);
392 Variable variable = installVariable(nameAndStream.getName(), nameAndStream.getStream(),
393 EnumSet.noneOf(Flags.class), locationDescription);
394 int value = Integer.parseInt(fields[1]);
395 variable.setOutput(value);
396 continue;
397 }
398 Object[] tokenisedRule = parse(trimmedLine, locationDescription);
399 if (null != tokenisedRule && tokenisedRule.length > 0)
400 {
401 this.tokenisedRules.add(tokenisedRule);
402 }
403 }
404 }
405
406
407
408
409
410
411 public void checkConsistency() throws SimRuntimeException, TrafficControlException
412 {
413 for (Variable v : this.variablesInDefinitionOrder)
414 {
415 if (0 == v.refCount && (!v.isOutput()) && (!v.getName().matches("^RA.")))
416 {
417
418 fireTimedEvent(TRAFFICCONTROL_CONTROLLER_WARNING,
419 new Object[] {getId(), v.toString(EnumSet.of(PrintFlags.ID)) + " is never referenced"},
420 this.simulator.getSimulatorTime());
421 }
422 if (!v.isDetector())
423 {
424 if (!v.getFlags().contains(Flags.HAS_START_RULE))
425 {
426
427 fireTimedEvent(TRAFFICCONTROL_CONTROLLER_WARNING,
428 new Object[] {getId(), v.toString(EnumSet.of(PrintFlags.ID)) + " has no start rule"},
429 this.simulator.getSimulatorTime());
430 }
431 if ((!v.getFlags().contains(Flags.HAS_END_RULE)) && (!v.isTimer()))
432 {
433
434 fireTimedEvent(TRAFFICCONTROL_CONTROLLER_WARNING,
435 new Object[] {getId(), v.toString(EnumSet.of(PrintFlags.ID)) + " has no end rule"},
436 this.simulator.getSimulatorTime());
437 }
438 }
439 }
440 Network network = null;
441 try
442 {
443 network = ((OTSModelInterface) this.simulator.getReplication().getExperiment().getModel()).getNetwork();
444 }
445 catch (ClassCastException e)
446 {
447 throw new SimRuntimeException("Model is not an OTSModelInterface");
448 }
449 ImmutableCollection<TrafficLight> trafficLights = network.getObjectMap(TrafficLight.class).values();
450 Map<String, List<TrafficLight>> trafficLightMap = new LinkedHashMap<>();
451 for (TrafficLight tl : trafficLights)
452 {
453 String trafficLightName = tl.getId();
454 if (trafficLightName.startsWith(getId()))
455 {
456 trafficLightName = trafficLightName.substring(getId().length());
457 if (trafficLightName.startsWith("."))
458 {
459 trafficLightName = trafficLightName.substring(1);
460 }
461 }
462 if (trafficLightName.substring(trafficLightName.length() - 2).startsWith("."))
463 {
464 trafficLightName = trafficLightName.substring(0, trafficLightName.length() - 2);
465 }
466 List<TrafficLight> list = trafficLightMap.get(trafficLightName);
467 if (null == list)
468 {
469 list = new ArrayList<>();
470 trafficLightMap.put(trafficLightName, list);
471 }
472 list.add(tl);
473 }
474 Map<String, TrafficLightSensor> sensors = new LinkedHashMap<>();
475
476 for (FlankSensor flankSensor : network.getObjectMap(FlankSensor.class).values())
477 {
478 TrafficLightSensor trafficLightSensor = flankSensor.getParent();
479 sensors.put(trafficLightSensor.getId(), trafficLightSensor);
480 }
481 for (Variable variable : this.variables.values())
482 {
483 if (variable.isOutput())
484 {
485 if (variable.getValue() != 0)
486 {
487 for (TrafficLight trafficLight : variable.getTrafficLights())
488 {
489 trafficLight.setTrafficLightColor(variable.getColor());
490 }
491 }
492 int added = 0;
493 String name = String.format("%s%02d", variable.getName(), variable.getStream());
494 String digits = String.format("%02d", variable.getStream());
495 List<TrafficLight> matchingLights = trafficLightMap.get(digits);
496 if (null == matchingLights)
497 {
498 throw new TrafficControlException("No traffic light for stream " + digits + " found");
499 }
500 for (TrafficLight tl : matchingLights)
501 {
502 try
503 {
504 variable.addOutput(tl);
505 }
506 catch (TrafficControlException exception)
507 {
508
509 exception.printStackTrace();
510 throw new SimRuntimeException(exception);
511 }
512 if (variable.getValue() != 0)
513 {
514 tl.setTrafficLightColor(variable.getColor());
515 }
516 added++;
517 }
518 if (0 == added)
519 {
520 throw new TrafficControlException("No traffic light found that matches output " + name + " and " + getId());
521 }
522 }
523 else if (variable.isDetector())
524 {
525 String name = variable.getName();
526 String subNumber = name.substring(name.length() - 1);
527 name = name.substring(0, name.length() - 1);
528 name = String.format("%s%02d", name, variable.getStream());
529 String digits = String.format("%02d", variable.getStream());
530 TrafficLightSensor tls = sensors.get("D" + digits + subNumber);
531 if (null == tls)
532 {
533 throw new TrafficControlException(
534 "No sensor found that matches " + name + " subNumber " + subNumber + " and " + getId());
535 }
536 variable.subscribeToDetector(tls);
537 if (null != this.stateDisplay)
538 {
539
540 EventListenerInterface eli =
541 this.stateDisplay.getDetectorImage(String.format("%02d.%s", variable.getStream(), subNumber));
542 if (null == eli)
543 {
544 throw new TrafficControlException("Cannor find detector image matching variable " + variable);
545 }
546
547 tls.addListener(eli, NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_ENTRY_EVENT);
548 tls.addListener(eli, NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_EXIT_EVENT);
549 }
550 }
551 }
552 }
553
554
555
556
557
558
559
560
561
562 private void addTrafCODDisplay(final List<String> rules) throws TrafficControlException, IOException
563 {
564 boolean useFirstCoordinates = true;
565 int lineno = 0;
566 for (String line : rules)
567 {
568 lineno++;
569 String rule = line.trim();
570 if (rule.length() == 0)
571 {
572 continue;
573 }
574 String[] fields = rule.split("=");
575 if ("mapfile".contentEquals(fields[0]))
576 {
577 if (fields[1].matches("[Bb][Mm][Pp]|[Pp][Nn][Gg]$"))
578 {
579 useFirstCoordinates = false;
580 }
581
582
583 }
584 else if ("light".equals(fields[0]))
585 {
586
587 int streamNumber;
588 try
589 {
590 streamNumber = Integer.parseInt(fields[1].substring(0, 2));
591 }
592 catch (NumberFormatException nfe)
593 {
594 throw new TrafficControlException("Bad traffic light number in coordinates: " + rule);
595 }
596
597 TrafficLightImage tli =
598 new TrafficLightImage(this.stateDisplay, getCoordinates(fields[1].substring(3), useFirstCoordinates),
599 String.format("Traffic Light %02d", streamNumber));
600 for (Variable v : this.variablesInDefinitionOrder)
601 {
602 if (v.isOutput() && v.getStream() == streamNumber)
603 {
604 v.addOutput(tli);
605 }
606 }
607 }
608 else if ("detector".equals(fields[0]))
609 {
610 int detectorStream;
611 int detectorSubNumber;
612 try
613 {
614 detectorStream = Integer.parseInt(fields[1].substring(0, 2));
615 detectorSubNumber = Integer.parseInt(fields[1].substring(3, 4));
616 }
617 catch (NumberFormatException nfe)
618 {
619 throw new TrafficControlException("Cannot parse detector number in coordinates " + rule);
620 }
621 String detectorName = String.format("D%02d%d", detectorStream, detectorSubNumber);
622 Variable detectorVariable = this.variables.get(detectorName);
623 if (null == detectorVariable)
624 {
625 throw new TrafficControlException(
626 "coordinates defines detector " + detectorName + " which does not exist in the TrafCOD program");
627 }
628
629 new DetectorImage(this.stateDisplay, getCoordinates(fields[1].substring(5), useFirstCoordinates),
630 String.format("%02d.%d", detectorStream, detectorSubNumber),
631 String.format("Detector %02d.%d", detectorStream, detectorSubNumber));
632 }
633 else
634 {
635 throw new TrafficControlException("Cannot parse coordinates line " + lineno + "in \"" + line + "\"");
636 }
637 }
638 }
639
640
641
642
643
644
645
646
647
648 private static Point2D getCoordinates(final String line, final boolean useFirstCoordinates) throws TrafficControlException
649 {
650 String work = line.replaceAll("[ ,\t]+", "\t").trim();
651 int x;
652 int y;
653 String[] fields = work.split("\t");
654 if (fields.length < (useFirstCoordinates ? 2 : 4))
655 {
656 throw new TrafficControlException("not enough fields in tfg line \"" + line + "\"");
657 }
658 try
659 {
660 x = Integer.parseInt(fields[useFirstCoordinates ? 0 : 2]);
661 y = Integer.parseInt(fields[useFirstCoordinates ? 1 : 3]);
662 }
663 catch (NumberFormatException nfe)
664 {
665 throw new TrafficControlException("Bad value in tfg line \"" + line + "\"");
666 }
667 return new Point2D.Double(x, y);
668 }
669
670
671
672
673
674
675 private int decrementTimers() throws TrafficControlException
676 {
677
678 int changeCount = 0;
679 for (Variable v : this.variables.values())
680 {
681 if (v.isTimer() && v.getValue() > 0 && v.decrementTimer(this.currentTime10))
682 {
683 changeCount++;
684 }
685 }
686 return changeCount;
687 }
688
689
690
691
692 private void resetTimerFlags()
693 {
694 for (Variable v : this.variablesInDefinitionOrder)
695 {
696 if (v.isTimer())
697 {
698 v.clearChangedFlag();
699 v.clearFlag(Flags.START);
700 v.clearFlag(Flags.END);
701 }
702 }
703 }
704
705
706
707
708
709
710 @SuppressWarnings("unused")
711 private void evalExprs() throws TrafficControlException, SimRuntimeException
712 {
713 fireTimedEvent(TrafficController.TRAFFICCONTROL_CONTROLLER_EVALUATING, new Object[] {getId()},
714 this.simulator.getSimulatorTime());
715
716
717
718
719
720
721
722
723
724
725
726
727
728 decrementTimers();
729 this.currentTime10 = (int) (this.simulator.getSimulatorTime().si * 10);
730 int loop;
731 for (loop = 0; loop < this.maxLoopCount; loop++)
732 {
733 int changeCount = evalExpressionsOnce();
734 resetTimerFlags();
735 if (changeCount == 0)
736 {
737 break;
738 }
739 }
740
741 if (loop >= this.maxLoopCount)
742 {
743 StringBuffer warningMessage = new StringBuffer();
744 warningMessage.append(String
745 .format("Control program did not settle to a final state in %d iterations; oscillating variables:", loop));
746 for (Variable v : this.variablesInDefinitionOrder)
747 {
748 if (v.getFlags().contains(Flags.CHANGED))
749 {
750 warningMessage.append(String.format(" %s%02d", v.getName(), v.getStream()));
751 }
752 }
753 fireTimedEvent(TrafficController.TRAFFICCONTROL_CONTROLLER_WARNING,
754 new Object[] {getId(), warningMessage.toString()}, this.simulator.getSimulatorTime());
755 }
756 this.simulator.scheduleEventRel(EVALUATION_INTERVAL, this, this, "evalExprs", null);
757 }
758
759
760
761
762
763
764 private int evalExpressionsOnce() throws TrafficControlException
765 {
766 for (Variable variable : this.variables.values())
767 {
768 variable.clearChangedFlag();
769 }
770 int changeCount = 0;
771 for (Object[] rule : this.tokenisedRules)
772 {
773 if (evalRule(rule))
774 {
775 changeCount++;
776 }
777 }
778 return changeCount;
779 }
780
781
782
783
784
785
786
787 private boolean evalRule(final Object[] rule) throws TrafficControlException
788 {
789 boolean result = false;
790 Token ruleType = (Token) rule[0];
791 Variable./../org/opentrafficsim/trafficcontrol/trafcod/TrafCOD.html#Variable">Variable destination = (Variable) rule[1];
792 if (destination.isTimer())
793 {
794 if (destination.getFlags().contains(Flags.TIMEREXPIRED))
795 {
796 destination.clearFlag(Flags.TIMEREXPIRED);
797 destination.setFlag(Flags.END);
798 }
799 else if (destination.getFlags().contains(Flags.START) || destination.getFlags().contains(Flags.END))
800 {
801 destination.clearFlag(Flags.START);
802 destination.clearFlag(Flags.END);
803 destination.setFlag(Flags.CHANGED);
804 }
805 }
806 else
807 {
808
809 if (Token.START_RULE == ruleType)
810 {
811 destination.clearFlag(Flags.START);
812 }
813 else if (Token.END_RULE == ruleType)
814 {
815 destination.clearFlag(Flags.END);
816 }
817 else
818 {
819 destination.clearFlag(Flags.START);
820 destination.clearFlag(Flags.END);
821 }
822 }
823
824 int currentValue = destination.getValue();
825 if (Token.START_RULE == ruleType && currentValue != 0 || Token.END == ruleType && currentValue == 0
826 || Token.INIT_TIMER == ruleType && currentValue != 0)
827 {
828 return false;
829 }
830 this.currentRule = rule;
831 this.currentToken = 2;
832 this.stack.clear();
833 evalExpr(0);
834 if (this.currentToken < this.currentRule.length && Token.CLOSE_PAREN == this.currentRule[this.currentToken])
835 {
836 throw new TrafficControlException("Too many closing parentheses");
837 }
838 int resultValue = pop();
839 if (Token.END_RULE == ruleType)
840 {
841
842 if (0 == resultValue)
843 {
844 resultValue = destination.getValue();
845 }
846 else
847 {
848 resultValue = 0;
849 }
850 }
851 if (resultValue != 0 && destination.getValue() == 0)
852 {
853 destination.setFlag(Flags.START);
854 }
855 else if (resultValue == 0 && destination.getValue() != 0)
856 {
857 destination.setFlag(Flags.END);
858 }
859 if (destination.isTimer())
860 {
861 if (resultValue != 0 && Token.END_RULE != ruleType)
862 {
863 if (0 == destination.getValue())
864 {
865 result = true;
866 }
867 int timerValue10 = destination.getTimerMax();
868 if (timerValue10 < 1)
869 {
870
871 timerValue10 = 1;
872 }
873 result = destination.setValue(timerValue10, this.currentTime10, new CausePrinter(rule), this);
874 }
875 else if (0 == resultValue && Token.END_RULE == ruleType && destination.getValue() != 0)
876 {
877 result = destination.setValue(0, this.currentTime10, new CausePrinter(rule), this);
878 }
879 }
880 else if (destination.getValue() != resultValue)
881 {
882 result = destination.setValue(resultValue, this.currentTime10, new CausePrinter(rule), this);
883 if (destination.isOutput())
884 {
885 fireEvent(TRAFFIC_LIGHT_CHANGED,
886 new Object[] {getId(), new Integer(destination.getStream()), destination.getColor()});
887 }
888 if (destination.isConflictGroup() && resultValue != 0)
889 {
890 int conflictGroupRank = destination.conflictGroupRank();
891 StringBuilder conflictGroupList = new StringBuilder();
892 for (Short stream : this.conflictGroups.get(conflictGroupRank))
893 {
894 if (conflictGroupList.length() > 0)
895 {
896 conflictGroupList.append(" ");
897 }
898 conflictGroupList.append(String.format("%02d", stream));
899 }
900 fireEvent(TRAFFICCONTROL_CONFLICT_GROUP_CHANGED,
901 new Object[] {getId(), this.currentConflictGroup, conflictGroupList.toString()});
902
903
904 this.currentConflictGroup = conflictGroupList.toString();
905 }
906 }
907 return result;
908 }
909
910
911 private static final int BIND_RELATIONAL_OPERATOR = 1;
912
913
914 private static final int BIND_ADDITION = 2;
915
916
917 private static final int BIND_MULTIPLY = 3;
918
919
920 private static int BIND_UNARY_MINUS = 4;
921
922
923
924
925
926
927
928
929
930
931
932
933
934 private void evalExpr(final int bindingStrength) throws TrafficControlException
935 {
936 if (this.currentToken >= this.currentRule.length)
937 {
938 throw new TrafficControlException("Missing operand at end of expression " + printRule(this.currentRule, false));
939 }
940 Token token = (Token) this.currentRule[this.currentToken++];
941 Object nextToken = null;
942 if (this.currentToken < this.currentRule.length)
943 {
944 nextToken = this.currentRule[this.currentToken];
945 }
946 switch (token)
947 {
948 case UNARY_MINUS:
949 if (Token.OPEN_PAREN != nextToken && Token.VARIABLE != nextToken && Token.NEG_VARIABLE != nextToken
950 && Token.CONSTANT != nextToken && Token.START != nextToken && Token.END != nextToken)
951 {
952 throw new TrafficControlException("Operand expected after unary minus");
953 }
954 evalExpr(BIND_UNARY_MINUS);
955 push(-pop());
956 break;
957
958 case OPEN_PAREN:
959 evalExpr(0);
960 if (Token.CLOSE_PAREN != this.currentRule[this.currentToken])
961 {
962 throw new TrafficControlException("Missing closing parenthesis");
963 }
964 this.currentToken++;
965 break;
966
967 case START:
968 if (Token.VARIABLE != nextToken || this.currentToken >= this.currentRule.length - 1)
969 {
970 throw new TrafficControlException("Missing variable after S");
971 }
972 nextToken = this.currentRule[++this.currentToken];
973 if (!(nextToken instanceof Variable))
974 {
975 throw new TrafficControlException("Missing variable after S");
976 }
977 push(((Variable) nextToken).getFlags().contains(Flags.START) ? 1 : 0);
978 this.currentToken++;
979 break;
980
981 case END:
982 if (Token.VARIABLE != nextToken || this.currentToken >= this.currentRule.length - 1)
983 {
984 throw new TrafficControlException("Missing variable after E");
985 }
986 nextToken = this.currentRule[++this.currentToken];
987 if (!(nextToken instanceof Variable))
988 {
989 throw new TrafficControlException("Missing variable after E");
990 }
991 push(((Variable) nextToken).getFlags().contains(Flags.END) ? 1 : 0);
992 this.currentToken++;
993 break;
994
995 case VARIABLE:
996 {
997 Variable../../../org/opentrafficsim/trafficcontrol/trafcod/TrafCOD.html#Variable">Variable operand = (Variable) nextToken;
998 if (operand.isTimer())
999 {
1000 push(operand.getValue() == 0 ? 0 : 1);
1001 }
1002 else
1003 {
1004 push(operand.getValue());
1005 }
1006 this.currentToken++;
1007 break;
1008 }
1009
1010 case CONSTANT:
1011 push((Integer) nextToken);
1012 this.currentToken++;
1013 break;
1014
1015 case NEG_VARIABLE:
1016 Variable../../../org/opentrafficsim/trafficcontrol/trafcod/TrafCOD.html#Variable">Variable operand = (Variable) nextToken;
1017 push(operand.getValue() == 0 ? 1 : 0);
1018 this.currentToken++;
1019 break;
1020
1021 default:
1022 throw new TrafficControlException("Operand missing");
1023 }
1024 evalRHS(bindingStrength);
1025 }
1026
1027
1028
1029
1030
1031
1032 private void evalRHS(final int bindingStrength) throws TrafficControlException
1033 {
1034 while (true)
1035 {
1036 if (this.currentToken >= this.currentRule.length)
1037 {
1038 return;
1039 }
1040 Token token = (Token) this.currentRule[this.currentToken];
1041 switch (token)
1042 {
1043 case CLOSE_PAREN:
1044 return;
1045
1046 case TIMES:
1047 if (BIND_MULTIPLY <= bindingStrength)
1048 {
1049 return;
1050 }
1051
1052
1053
1054
1055
1056 this.currentToken++;
1057 evalExpr(BIND_MULTIPLY);
1058 push(pop() * pop() == 0 ? 0 : 1);
1059 break;
1060
1061 case EQ:
1062 case NOTEQ:
1063 case LE:
1064 case LEEQ:
1065 case GT:
1066 case GTEQ:
1067 if (BIND_RELATIONAL_OPERATOR <= bindingStrength)
1068 {
1069 return;
1070 }
1071
1072
1073
1074
1075
1076 this.currentToken++;
1077 evalExpr(BIND_RELATIONAL_OPERATOR);
1078 switch (token)
1079 {
1080 case EQ:
1081 push(pop() == pop() ? 1 : 0);
1082 break;
1083
1084 case NOTEQ:
1085 push(pop() != pop() ? 1 : 0);
1086 break;
1087
1088 case GT:
1089 push(pop() < pop() ? 1 : 0);
1090 break;
1091
1092 case GTEQ:
1093 push(pop() <= pop() ? 1 : 0);
1094 break;
1095
1096 case LE:
1097 push(pop() > pop() ? 1 : 0);
1098 break;
1099
1100 case LEEQ:
1101 push(pop() >= pop() ? 1 : 0);
1102 break;
1103
1104 default:
1105 throw new TrafficControlException("Bad relational operator");
1106 }
1107 break;
1108
1109 case PLUS:
1110 if (BIND_ADDITION <= bindingStrength)
1111 {
1112 return;
1113 }
1114
1115
1116
1117
1118
1119 this.currentToken++;
1120 evalExpr(BIND_ADDITION);
1121 push(pop() + pop() == 0 ? 0 : 1);
1122 break;
1123
1124 case MINUS:
1125 if (BIND_ADDITION <= bindingStrength)
1126 {
1127 return;
1128 }
1129
1130
1131
1132
1133
1134 this.currentToken++;
1135 evalExpr(BIND_ADDITION);
1136 push(-pop() + pop());
1137 break;
1138
1139 default:
1140 throw new TrafficControlException("Missing binary operator");
1141 }
1142 }
1143 }
1144
1145
1146
1147
1148
1149 private void push(final int value)
1150 {
1151 this.stack.add(value);
1152 }
1153
1154
1155
1156
1157
1158
1159 private int pop() throws TrafficControlException
1160 {
1161 if (this.stack.size() < 1)
1162 {
1163 throw new TrafficControlException("Stack empty");
1164 }
1165 return this.stack.remove(this.stack.size() - 1);
1166 }
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176 static String printRule(Object[] tokens, final boolean printValues) throws TrafficControlException
1177 {
1178 EnumSet<PrintFlags> variableFlags = EnumSet.of(PrintFlags.ID);
1179 if (printValues)
1180 {
1181 variableFlags.add(PrintFlags.VALUE);
1182 }
1183 EnumSet<PrintFlags> negatedVariableFlags = EnumSet.copyOf(variableFlags);
1184 negatedVariableFlags.add(PrintFlags.NEGATED);
1185 StringBuilder result = new StringBuilder();
1186 for (int inPos = 0; inPos < tokens.length; inPos++)
1187 {
1188 Object token = tokens[inPos];
1189 if (token instanceof Token)
1190 {
1191 switch ((Token) token)
1192 {
1193 case EQUALS_RULE:
1194 result.append(((Variable) tokens[++inPos]).toString(variableFlags));
1195 result.append("=");
1196 break;
1197
1198 case NEG_EQUALS_RULE:
1199 result.append(((Variable) tokens[++inPos]).toString(negatedVariableFlags));
1200 result.append("=");
1201 break;
1202
1203 case START_RULE:
1204 result.append(((Variable) tokens[++inPos]).toString(variableFlags));
1205 result.append(".=");
1206 break;
1207
1208 case END_RULE:
1209 result.append(((Variable) tokens[++inPos]).toString(variableFlags));
1210 result.append("N.=");
1211 break;
1212
1213 case INIT_TIMER:
1214 result.append(((Variable) tokens[++inPos]).toString(EnumSet.of(PrintFlags.ID, PrintFlags.INITTIMER)));
1215 result.append(".=");
1216 break;
1217
1218 case REINIT_TIMER:
1219 result.append(((Variable) tokens[++inPos]).toString(EnumSet.of(PrintFlags.ID, PrintFlags.REINITTIMER)));
1220 result.append(".=");
1221 break;
1222
1223 case START:
1224 result.append("S");
1225 break;
1226
1227 case END:
1228 result.append("E");
1229 break;
1230
1231 case VARIABLE:
1232 result.append(((Variable) tokens[++inPos]).toString(variableFlags));
1233 break;
1234
1235 case NEG_VARIABLE:
1236 result.append(((Variable) tokens[++inPos]).toString(variableFlags));
1237 result.append("N");
1238 break;
1239
1240 case CONSTANT:
1241 result.append(tokens[++inPos]).toString();
1242 break;
1243
1244 case UNARY_MINUS:
1245 case MINUS:
1246 result.append("-");
1247 break;
1248
1249 case PLUS:
1250 result.append("+");
1251 break;
1252
1253 case TIMES:
1254 result.append(".");
1255 break;
1256
1257 case EQ:
1258 result.append("=");
1259 break;
1260
1261 case NOTEQ:
1262 result.append("<>");
1263 break;
1264
1265 case GT:
1266 result.append(">");
1267 break;
1268
1269 case GTEQ:
1270 result.append(">=");
1271 break;
1272
1273 case LE:
1274 result.append("<");
1275 break;
1276
1277 case LEEQ:
1278 result.append("<=");
1279 break;
1280
1281 case OPEN_PAREN:
1282 result.append("(");
1283 break;
1284
1285 case CLOSE_PAREN:
1286 result.append(")");
1287 break;
1288
1289 default:
1290 System.out.println(
1291 "<<<ERROR>>> encountered a non-Token object: " + token + " after " + result.toString());
1292 throw new TrafficControlException("Unknown token");
1293 }
1294 }
1295 else
1296 {
1297 System.out.println("<<<ERROR>>> encountered a non-Token object: " + token + " after " + result.toString());
1298 throw new TrafficControlException("Not a token");
1299 }
1300 }
1301 return result.toString();
1302 }
1303
1304
1305
1306
1307
1308
1309 enum ParserState
1310 {
1311
1312 FIND_LHS,
1313
1314 FIND_ASSIGN,
1315
1316 FIND_RHS,
1317
1318 MAY_UMINUS,
1319
1320 FIND_EXPR,
1321 }
1322
1323
1324
1325
1326
1327
1328 enum Token
1329 {
1330
1331 EQUALS_RULE,
1332
1333 NEG_EQUALS_RULE,
1334
1335 ASSIGNMENT,
1336
1337 START_RULE,
1338
1339 END_RULE,
1340
1341 INIT_TIMER,
1342
1343 REINIT_TIMER,
1344
1345 UNARY_MINUS,
1346
1347 LEEQ,
1348
1349 NOTEQ,
1350
1351 LE,
1352
1353 GTEQ,
1354
1355 GT,
1356
1357 EQ,
1358
1359 START,
1360
1361 END,
1362
1363 VARIABLE,
1364
1365 NEG_VARIABLE,
1366
1367 CONSTANT,
1368
1369 PLUS,
1370
1371 MINUS,
1372
1373 TIMES,
1374
1375 OPEN_PAREN,
1376
1377 CLOSE_PAREN,
1378 }
1379
1380
1381
1382
1383
1384
1385
1386
1387 private Object[] parse(final String rawRule, final String locationDescription) throws TrafficControlException
1388 {
1389 if (rawRule.length() == 0)
1390 {
1391 throw new TrafficControlException("empty rule at " + locationDescription);
1392 }
1393 ParserState state = ParserState.FIND_LHS;
1394 String rule = rawRule.toUpperCase(Locale.US);
1395 Token ruleType = Token.ASSIGNMENT;
1396 int inPos = 0;
1397 NameAndStream lhsNameAndStream = null;
1398 List<Object> tokens = new ArrayList<>();
1399 while (inPos < rule.length())
1400 {
1401 char character = rule.charAt(inPos);
1402 if (Character.isWhitespace(character))
1403 {
1404 inPos++;
1405 continue;
1406 }
1407 switch (state)
1408 {
1409 case FIND_LHS:
1410 {
1411 if ('S' == character)
1412 {
1413 ruleType = Token.START_RULE;
1414 inPos++;
1415 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1416 inPos += lhsNameAndStream.getNumberOfChars();
1417 }
1418 else if ('E' == character)
1419 {
1420 ruleType = Token.END_RULE;
1421 inPos++;
1422 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1423 inPos += lhsNameAndStream.getNumberOfChars();
1424 }
1425 else if ('I' == character && 'T' == rule.charAt(inPos + 1))
1426 {
1427 ruleType = Token.INIT_TIMER;
1428 inPos++;
1429 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1430 inPos += lhsNameAndStream.getNumberOfChars();
1431 }
1432 else if ('R' == character && 'I' == rule.charAt(inPos + 1) && 'T' == rule.charAt(inPos + 2))
1433 {
1434 ruleType = Token.REINIT_TIMER;
1435 inPos += 2;
1436 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1437 inPos += lhsNameAndStream.getNumberOfChars();
1438 }
1439 else if ('T' == character && rule.indexOf('=') >= 0
1440 && (rule.indexOf('N') < 0 || rule.indexOf('N') > rule.indexOf('=')))
1441 {
1442 throw new TrafficControlException("Bad time initialization at " + locationDescription);
1443 }
1444 else
1445 {
1446 ruleType = Token.EQUALS_RULE;
1447 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1448 inPos += lhsNameAndStream.getNumberOfChars();
1449 if (lhsNameAndStream.isNegated())
1450 {
1451 ruleType = Token.NEG_EQUALS_RULE;
1452 }
1453 }
1454 state = ParserState.FIND_ASSIGN;
1455 break;
1456 }
1457
1458 case FIND_ASSIGN:
1459 {
1460 if ('.' == character && '=' == rule.charAt(inPos + 1))
1461 {
1462 if (Token.EQUALS_RULE == ruleType)
1463 {
1464 ruleType = Token.START_RULE;
1465 }
1466 else if (Token.NEG_EQUALS_RULE == ruleType)
1467 {
1468 ruleType = Token.END_RULE;
1469 }
1470 inPos += 2;
1471 }
1472 else if ('=' == character)
1473 {
1474 if (Token.START_RULE == ruleType || Token.END_RULE == ruleType || Token.INIT_TIMER == ruleType
1475 || Token.REINIT_TIMER == ruleType)
1476 {
1477 throw new TrafficControlException("Bad assignment at " + locationDescription);
1478 }
1479 inPos++;
1480 }
1481 tokens.add(ruleType);
1482 EnumSet<Flags> lhsFlags = EnumSet.noneOf(Flags.class);
1483 if (Token.START_RULE == ruleType || Token.EQUALS_RULE == ruleType || Token.NEG_EQUALS_RULE == ruleType
1484 || Token.INIT_TIMER == ruleType || Token.REINIT_TIMER == ruleType)
1485 {
1486 lhsFlags.add(Flags.HAS_START_RULE);
1487 }
1488 if (Token.END_RULE == ruleType || Token.EQUALS_RULE == ruleType || Token.NEG_EQUALS_RULE == ruleType)
1489 {
1490 lhsFlags.add(Flags.HAS_END_RULE);
1491 }
1492 Variable lhsVariable = installVariable(lhsNameAndStream.getName(), lhsNameAndStream.getStream(), lhsFlags,
1493 locationDescription);
1494 tokens.add(lhsVariable);
1495 state = ParserState.MAY_UMINUS;
1496 break;
1497 }
1498
1499 case MAY_UMINUS:
1500 if ('-' == character)
1501 {
1502 tokens.add(Token.UNARY_MINUS);
1503 inPos++;
1504 }
1505 state = ParserState.FIND_EXPR;
1506 break;
1507
1508 case FIND_EXPR:
1509 {
1510 if (Character.isDigit(character))
1511 {
1512 int constValue = 0;
1513 while (inPos < rule.length() && Character.isDigit(rule.charAt(inPos)))
1514 {
1515 int digit = rule.charAt(inPos) - '0';
1516 if (constValue >= (Integer.MAX_VALUE - digit) / 10)
1517 {
1518 throw new TrafficControlException("Number too large at " + locationDescription);
1519 }
1520 constValue = 10 * constValue + digit;
1521 inPos++;
1522 }
1523 tokens.add(Token.CONSTANT);
1524 tokens.add(new Integer(constValue));
1525 }
1526 if (inPos >= rule.length())
1527 {
1528 return tokens.toArray();
1529 }
1530 character = rule.charAt(inPos);
1531 switch (character)
1532 {
1533 case '+':
1534 tokens.add(Token.PLUS);
1535 inPos++;
1536 break;
1537
1538 case '-':
1539 tokens.add(Token.MINUS);
1540 inPos++;
1541 break;
1542
1543 case '.':
1544 tokens.add(Token.TIMES);
1545 inPos++;
1546 break;
1547
1548 case ')':
1549 tokens.add(Token.CLOSE_PAREN);
1550 inPos++;
1551 break;
1552
1553 case '<':
1554 {
1555 Character nextChar = rule.charAt(++inPos);
1556 if ('=' == nextChar)
1557 {
1558 tokens.add(Token.LEEQ);
1559 inPos++;
1560 }
1561 else if ('>' == nextChar)
1562 {
1563 tokens.add(Token.NOTEQ);
1564 inPos++;
1565 }
1566 else
1567 {
1568 tokens.add(Token.LE);
1569 }
1570 break;
1571 }
1572
1573 case '>':
1574 {
1575 Character nextChar = rule.charAt(++inPos);
1576 if ('=' == nextChar)
1577 {
1578 tokens.add(Token.GTEQ);
1579 inPos++;
1580 }
1581 else if ('<' == nextChar)
1582 {
1583 tokens.add(Token.NOTEQ);
1584 inPos++;
1585 }
1586 else
1587 {
1588 tokens.add(Token.GT);
1589 }
1590 break;
1591 }
1592
1593 case '=':
1594 {
1595 Character nextChar = rule.charAt(++inPos);
1596 if ('<' == nextChar)
1597 {
1598 tokens.add(Token.LEEQ);
1599 inPos++;
1600 }
1601 else if ('>' == nextChar)
1602 {
1603 tokens.add(Token.GTEQ);
1604 inPos++;
1605 }
1606 else
1607 {
1608 tokens.add(Token.EQ);
1609 }
1610 break;
1611 }
1612
1613 case '(':
1614 {
1615 inPos++;
1616 tokens.add(Token.OPEN_PAREN);
1617 state = ParserState.MAY_UMINUS;
1618 break;
1619 }
1620
1621 default:
1622 {
1623 if ('S' == character)
1624 {
1625 tokens.add(Token.START);
1626 inPos++;
1627 }
1628 else if ('E' == character)
1629 {
1630 tokens.add(Token.END);
1631 inPos++;
1632 }
1633 NameAndStreaml/trafcod/TrafCOD.html#NameAndStream">NameAndStream nas = new NameAndStream(rule.substring(inPos), locationDescription);
1634 inPos += nas.getNumberOfChars();
1635 if (nas.isNegated())
1636 {
1637 tokens.add(Token.NEG_VARIABLE);
1638 }
1639 else
1640 {
1641 tokens.add(Token.VARIABLE);
1642 }
1643 Variable variable = installVariable(nas.getName(), nas.getStream(), EnumSet.noneOf(Flags.class),
1644 locationDescription);
1645 variable.incrementReferenceCount();
1646 tokens.add(variable);
1647 }
1648 }
1649 break;
1650 }
1651 default:
1652 throw new TrafficControlException("Error: bad switch; case " + state + " should not happen");
1653 }
1654 }
1655 return tokens.toArray();
1656 }
1657
1658
1659
1660
1661
1662
1663
1664 private boolean stringBeginsWithIgnoreCase(final String sought, final String supplied)
1665 {
1666 if (sought.length() > supplied.length())
1667 {
1668 return false;
1669 }
1670 return (sought.equalsIgnoreCase(supplied.substring(0, sought.length())));
1671 }
1672
1673
1674
1675
1676
1677
1678
1679 private String variableKey(final String name, final short stream)
1680 {
1681 if (name.startsWith("D"))
1682 {
1683 return String.format("D%02d%s", stream, name.substring(1));
1684 }
1685 return String.format("%s%02d", name.toUpperCase(Locale.US), stream);
1686 }
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698 private Variable installVariable(String name, short stream, EnumSet<Flags> flags, String location)
1699 throws TrafficControlException
1700 {
1701 EnumSet<Flags> forbidden = EnumSet.complementOf(EnumSet.of(Flags.HAS_START_RULE, Flags.HAS_END_RULE));
1702 EnumSet<Flags> badFlags = EnumSet.copyOf(forbidden);
1703 badFlags.retainAll(flags);
1704 if (badFlags.size() > 0)
1705 {
1706 throw new TrafficControlException("installVariable was called with wrong flag(s): " + badFlags);
1707 }
1708 String key = variableKey(name, stream);
1709 Variable variable = this.variables.get(key);
1710 if (null == variable)
1711 {
1712
1713 variable = new Variable(name, stream, this);
1714 this.variables.put(key, variable);
1715 this.variablesInDefinitionOrder.add(variable);
1716 if (variable.isDetector())
1717 {
1718 this.detectors.put(key, variable);
1719 }
1720 }
1721 if (flags.contains(Flags.HAS_START_RULE))
1722 {
1723 variable.setStartSource(location);
1724 }
1725 if (flags.contains(Flags.HAS_END_RULE))
1726 {
1727 variable.setEndSource(location);
1728 }
1729 return variable;
1730 }
1731
1732
1733
1734
1735
1736 public SimulatorInterface<Time, Duration, SimTimeDoubleUnit> getSimulator()
1737 {
1738 return this.simulator;
1739 }
1740
1741
1742
1743
1744
1745 public int getStructureNumber()
1746 {
1747 return this.structureNumber;
1748 }
1749
1750
1751 @Override
1752 public void updateDetector(String detectorId, boolean detectingGTU)
1753 {
1754 Variable detector = this.detectors.get(detectorId);
1755 detector.setValue(detectingGTU ? 1 : 0, this.currentTime10,
1756 new CausePrinter(
1757 String.format("Detector %s becoming %s", detectorId, (detectingGTU ? "occupied" : "unoccupied"))),
1758 this);
1759 }
1760
1761
1762
1763
1764
1765
1766
1767
1768 public void traceVariablesOfStream(final int stream, final boolean trace)
1769 {
1770 for (Variable v : this.variablesInDefinitionOrder)
1771 {
1772 if (v.getStream() == stream)
1773 {
1774 if (trace)
1775 {
1776 v.setFlag(Flags.TRACED);
1777 }
1778 else
1779 {
1780 v.clearFlag(Flags.TRACED);
1781 }
1782 }
1783 }
1784 }
1785
1786
1787
1788
1789
1790
1791
1792
1793 public void traceVariable(final String variableName, final int stream, final boolean trace)
1794 {
1795 for (Variable v : this.variablesInDefinitionOrder)
1796 {
1797 if (v.getStream() == stream && variableName.equals(v.getName()))
1798 {
1799 if (trace)
1800 {
1801 v.setFlag(Flags.TRACED);
1802 }
1803 else
1804 {
1805 v.clearFlag(Flags.TRACED);
1806 }
1807 }
1808 }
1809 }
1810
1811
1812 @Override
1813 public void notify(EventInterface event) throws RemoteException
1814 {
1815 System.out.println("TrafCOD: received an event");
1816 if (event.getType().equals(TrafficController.TRAFFICCONTROL_SET_TRACING))
1817 {
1818 Object content = event.getContent();
1819 if (!(content instanceof Object[]))
1820 {
1821 System.err.println("TrafCOD controller " + getId() + " received event with bad payload (" + content + ")");
1822 return;
1823 }
1824 Object[] fields = (Object[]) event.getContent();
1825 if (getId().equals(fields[0]))
1826 {
1827 if (fields.length < 4 || !(fields[1] instanceof String) || !(fields[2] instanceof Integer)
1828 || !(fields[3] instanceof Boolean))
1829 {
1830 System.err.println("TrafCOD controller " + getId() + " received event with bad payload (" + content + ")");
1831 return;
1832 }
1833 String name = (String) fields[1];
1834 int stream = (Integer) fields[2];
1835 boolean trace = (Boolean) fields[3];
1836 if (name.length() > 1)
1837 {
1838 Variable v = this.variables.get(variableKey(name, (short) stream));
1839 if (null == v)
1840 {
1841 System.err.println("Received trace notification for nonexistent variable (name=\"" + name
1842 + "\", stream=" + stream + ")");
1843 }
1844 if (trace)
1845 {
1846 v.setFlag(Flags.TRACED);
1847 }
1848 else
1849 {
1850 v.clearFlag(Flags.TRACED);
1851 }
1852 }
1853 else
1854 {
1855 for (Variable v : this.variablesInDefinitionOrder)
1856 {
1857 if (v.getStream() == stream)
1858 {
1859 if (trace)
1860 {
1861 v.setFlag(Flags.TRACED);
1862 }
1863 else
1864 {
1865 v.clearFlag(Flags.TRACED);
1866 }
1867 }
1868 }
1869 }
1870 }
1871
1872 }
1873
1874 }
1875
1876
1877
1878
1879
1880
1881 void fireTrafCODEvent(final EventType eventType, final Object[] payload)
1882 {
1883 fireTimedEvent(eventType, payload, getSimulator().getSimulatorTime());
1884 }
1885
1886
1887 @Override
1888 public String getFullId()
1889 {
1890 return getId();
1891 }
1892
1893
1894 @Override
1895 public final InvisibleObjectInterface clone(final OTSSimulatorInterface newSimulator, final Network newNetwork)
1896 throws NetworkException
1897 {
1898 try
1899 {
1900
1901 TrafCODtrol/trafcod/TrafCOD.html#TrafCOD">TrafCOD result = new TrafCOD(getId(), this.trafCODRules, newSimulator, null, this.displayBackground, null);
1902 result.fireTimedEvent(TRAFFICCONTROL_CONTROLLER_CREATED,
1903 new Serializable[] {getId(), TrafficController.BEING_CLONED}, newSimulator.getSimulatorTime());
1904
1905 for (Variable v : this.variablesInDefinitionOrder)
1906 {
1907 Variable clonedVariable = result.installVariable(v.getName(), v.getStream(), EnumSet.noneOf(Flags.class), null);
1908 clonedVariable.setStartSource(v.getStartSource());
1909 clonedVariable.setEndSource(v.getEndSource());
1910 if (clonedVariable.isDetector())
1911 {
1912 String detectorName = clonedVariable.toString(EnumSet.of(PrintFlags.ID));
1913 int detectorNumber = clonedVariable.getStream() * 10 + detectorName.charAt(detectorName.length() - 1) - '0';
1914 TrafficLightSensor clonedSensor = null;
1915 for (ObjectInterface oi : newNetwork.getObjectMap().values())
1916 {
1917 if (oi instanceof TrafficLightSensor)
1918 {
1919 TrafficLightSensor tls = (TrafficLightSensor) oi;
1920 if (tls.getId().endsWith(detectorName))
1921 {
1922 clonedSensor = tls;
1923 }
1924 }
1925 }
1926 if (null == clonedSensor)
1927 {
1928 throw new TrafficControlException("Cannot find detector " + detectorName + " with number "
1929 + detectorNumber + " among the provided sensors");
1930 }
1931 clonedVariable.subscribeToDetector(clonedSensor);
1932 }
1933 clonedVariable.cloneState(v, newNetwork);
1934 String key = variableKey(clonedVariable.getName(), clonedVariable.getStream());
1935 result.variables.put(key, clonedVariable);
1936 }
1937 return result;
1938 }
1939 catch (TrafficControlException | SimRuntimeException tce)
1940 {
1941 throw new NetworkException(
1942 "Internal error; caught an unexpected TrafficControlException or SimRunTimeException in clone");
1943 }
1944 }
1945
1946
1947 @Override
1948 public Serializable getSourceId()
1949 {
1950 return null;
1951 }
1952
1953 }
1954
1955
1956
1957
1958 class NameAndStream
1959 {
1960
1961 private final String name;
1962
1963
1964 private short stream = TrafficController.NO_STREAM;
1965
1966
1967 private int numberOfChars = 0;
1968
1969
1970 private boolean negated = false;
1971
1972
1973
1974
1975
1976
1977
1978 public NameAndStream(final String text, final String locationDescription) throws TrafficControlException
1979 {
1980 int pos = 0;
1981 while (pos < text.length() && Character.isWhitespace(text.charAt(pos)))
1982 {
1983 pos++;
1984 }
1985 while (pos < text.length())
1986 {
1987 char character = text.charAt(pos);
1988 if (!Character.isLetterOrDigit(character))
1989 {
1990 break;
1991 }
1992 pos++;
1993 }
1994 this.numberOfChars = pos;
1995 String trimmed = text.substring(0, pos).replaceAll(" ", "");
1996 if (trimmed.length() == 0)
1997 {
1998 throw new TrafficControlException("missing variable at " + locationDescription);
1999 }
2000 if (trimmed.matches("^D([Nn]?\\d\\d\\d)|(\\d\\d\\d[Nn])"))
2001 {
2002
2003 if (trimmed.charAt(1) == 'N' || trimmed.charAt(1) == 'n')
2004 {
2005
2006 trimmed = "D" + trimmed.substring(2, 5) + "N" + trimmed.substring(5);
2007 this.negated = true;
2008 }
2009 this.name = "D" + trimmed.charAt(3);
2010 this.stream = (short) (10 * (trimmed.charAt(1) - '0') + trimmed.charAt(2) - '0');
2011 return;
2012 }
2013 StringBuilder nameBuilder = new StringBuilder();
2014 for (pos = 0; pos < trimmed.length(); pos++)
2015 {
2016 char nextChar = trimmed.charAt(pos);
2017 if (pos < trimmed.length() - 1 && Character.isDigit(nextChar) && Character.isDigit(trimmed.charAt(pos + 1))
2018 && TrafficController.NO_STREAM == this.stream)
2019 {
2020 if (0 == pos || (1 == pos && trimmed.startsWith("N")))
2021 {
2022 throw new TrafficControlException("Bad variable name: " + trimmed + " at " + locationDescription);
2023 }
2024 if (trimmed.charAt(pos - 1) == 'N')
2025 {
2026
2027 nameBuilder.deleteCharAt(nameBuilder.length() - 1);
2028
2029 trimmed =
2030 trimmed.substring(0, pos - 1) + trimmed.substring(pos, pos + 2) + trimmed.substring(pos + 2) + "N";
2031 pos--;
2032 }
2033 this.stream = (short) (10 * (trimmed.charAt(pos) - '0') + trimmed.charAt(pos + 1) - '0');
2034 pos++;
2035 }
2036 else
2037 {
2038 nameBuilder.append(nextChar);
2039 }
2040 }
2041 if (trimmed.endsWith("N"))
2042 {
2043 nameBuilder.deleteCharAt(nameBuilder.length() - 1);
2044 this.negated = true;
2045 }
2046 this.name = nameBuilder.toString();
2047 }
2048
2049
2050
2051
2052
2053 public boolean isNegated()
2054 {
2055 return this.negated;
2056 }
2057
2058
2059
2060
2061
2062 public short getStream()
2063 {
2064 return this.stream;
2065 }
2066
2067
2068
2069
2070
2071 public String getName()
2072 {
2073 return this.name;
2074 }
2075
2076
2077
2078
2079
2080 public int getNumberOfChars()
2081 {
2082 return this.numberOfChars;
2083 }
2084
2085
2086 @Override
2087 public String toString()
2088 {
2089 return "NameAndStream [name=" + this.name + ", stream=" + this.stream + ", numberOfChars=" + this.numberOfChars
2090 + ", negated=" + this.negated + "]";
2091 }
2092
2093 }
2094
2095
2096
2097
2098 class Variable implements EventListenerInterface
2099 {
2100
2101 private final TrafCOD trafCOD;
2102
2103
2104 EnumSet<Flags> flags = EnumSet.noneOf(Flags.class);
2105
2106
2107 int value;
2108
2109
2110 int timerMax10;
2111
2112
2113 TrafficLightColor color;
2114
2115
2116 final String name;
2117
2118
2119 final short stream;
2120
2121
2122 int refCount;
2123
2124
2125 int updateTime10;
2126
2127
2128 String startSource;
2129
2130
2131 String endSource;
2132
2133
2134 private Set<TrafficLight> trafficLights;
2135
2136
2137 private static String ROWLETTERS = "ABCDXYZUVW";
2138
2139
2140
2141
2142
2143
2144
2145
2146 final Variable clone(final OTSNetwork newNetwork, final TrafCOD newTrafCOD) throws NetworkException, TrafficControlException
2147 {
2148 Variablerol/trafcod/TrafCOD.html#Variable">Variable result = new Variable(getName(), getStream(), newTrafCOD);
2149 result.flags = EnumSet.copyOf(this.flags);
2150 result.value = this.value;
2151 result.timerMax10 = this.timerMax10;
2152 result.color = this.color;
2153 result.refCount = this.refCount;
2154 result.updateTime10 = this.updateTime10;
2155 result.startSource = this.startSource;
2156 result.endSource = this.endSource;
2157 for (TrafficLight tl : this.trafficLights)
2158 {
2159 if (tl instanceof TrafficLightImage)
2160 {
2161
2162 continue;
2163 }
2164 ObjectInterface clonedTrafficLight = newNetwork.getObjectMap().get(tl.getId());
2165 Throw.when(null == clonedTrafficLight, NetworkException.class,
2166 "Cannot find clone of traffic light %s in newNetwork", tl.getId());
2167 Throw.when(!(clonedTrafficLight instanceof TrafficLight), NetworkException.class,
2168 "Object %s in newNetwork is not a TrafficLight", clonedTrafficLight);
2169 result.addOutput((TrafficLight) clonedTrafficLight);
2170 }
2171 return result;
2172 }
2173
2174
2175
2176
2177
2178
2179 public Set<TrafficLight> getTrafficLights()
2180 {
2181 return this.trafficLights;
2182 }
2183
2184
2185
2186
2187
2188
2189
2190 public Variable(final String name, final short stream, TrafCOD trafCOD)
2191 {
2192 this.name = name.toUpperCase(Locale.US);
2193 this.stream = stream;
2194 this.trafCOD = trafCOD;
2195 if (this.name.startsWith("T"))
2196 {
2197 this.flags.add(Flags.IS_TIMER);
2198 }
2199 if (this.name.length() == 2 && this.name.startsWith("D") && Character.isDigit(this.name.charAt(1)))
2200 {
2201 this.flags.add(Flags.IS_DETECTOR);
2202 }
2203 if (TrafficController.NO_STREAM == stream && this.name.startsWith("MR") && this.name.length() == 3
2204 && ROWLETTERS.indexOf(this.name.charAt(2)) >= 0)
2205 {
2206 this.flags.add(Flags.CONFLICT_GROUP);
2207 }
2208 }
2209
2210
2211
2212
2213
2214 public String getName()
2215 {
2216 return this.name;
2217 }
2218
2219
2220
2221
2222
2223
2224 public void subscribeToDetector(TrafficLightSensor sensor) throws TrafficControlException
2225 {
2226 if (!isDetector())
2227 {
2228 throw new TrafficControlException("Cannot subscribe a non-detector to a TrafficLightSensor");
2229 }
2230 sensor.addListener(this, NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_ENTRY_EVENT);
2231 sensor.addListener(this, NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_EXIT_EVENT);
2232 }
2233
2234
2235
2236
2237 public void initialize()
2238 {
2239 if (this.flags.contains(Flags.INITED))
2240 {
2241 if (isTimer())
2242 {
2243 setValue(this.timerMax10, 0, new CausePrinter("Timer initialization rule"), this.trafCOD);
2244 }
2245 else
2246 {
2247 setValue(1, 0, new CausePrinter("Variable initialization rule"), this.trafCOD);
2248 }
2249 }
2250 }
2251
2252
2253
2254
2255
2256
2257
2258
2259 public boolean decrementTimer(final int timeStamp10) throws TrafficControlException
2260 {
2261 if (!isTimer())
2262 {
2263 throw new TrafficControlException("Variable " + this + " is not a timer");
2264 }
2265 if (this.value <= 0)
2266 {
2267 return false;
2268 }
2269 if (0 == --this.value)
2270 {
2271 this.flags.add(Flags.CHANGED);
2272 this.flags.add(Flags.END);
2273 this.value = 0;
2274 this.updateTime10 = timeStamp10;
2275 if (this.flags.contains(Flags.TRACED))
2276 {
2277 System.out.println("Timer " + toString() + " expired");
2278 }
2279 return true;
2280 }
2281 return false;
2282 }
2283
2284
2285
2286
2287
2288
2289 public TrafficLightColor getColor() throws TrafficControlException
2290 {
2291 if (!this.flags.contains(Flags.IS_OUTPUT))
2292 {
2293 throw new TrafficControlException("Stream " + this.toString() + "is not an output");
2294 }
2295 return this.color;
2296 }
2297
2298
2299
2300
2301
2302 public boolean isOutput()
2303 {
2304 return this.flags.contains(Flags.IS_OUTPUT);
2305 }
2306
2307
2308
2309
2310
2311 public boolean isConflictGroup()
2312 {
2313 return this.flags.contains(Flags.CONFLICT_GROUP);
2314 }
2315
2316
2317
2318
2319
2320
2321 public int conflictGroupRank() throws TrafficControlException
2322 {
2323 if (!isConflictGroup())
2324 {
2325 throw new TrafficControlException("Variable " + this + " is not a conflict group identifier");
2326 }
2327 return ROWLETTERS.indexOf(this.name.charAt(2));
2328 }
2329
2330
2331
2332
2333
2334 public boolean isDetector()
2335 {
2336 return this.flags.contains(Flags.IS_DETECTOR);
2337 }
2338
2339
2340
2341
2342
2343
2344
2345
2346 public boolean setValue(int newValue, int timeStamp10, CausePrinter cause, TrafCOD trafCOD)
2347 {
2348 boolean result = false;
2349 if (this.value != newValue)
2350 {
2351 this.updateTime10 = timeStamp10;
2352 setFlag(Flags.CHANGED);
2353 if (0 == newValue)
2354 {
2355 setFlag(Flags.END);
2356 result = true;
2357 }
2358 else if (!isTimer() || 0 == this.value)
2359 {
2360 setFlag(Flags.START);
2361 result = true;
2362 }
2363 if (isOutput() && newValue != 0)
2364 {
2365 for (TrafficLight trafficLight : this.trafficLights)
2366 {
2367 trafficLight.setTrafficLightColor(this.color);
2368 }
2369 }
2370 }
2371 if (this.flags.contains(Flags.TRACED))
2372 {
2373
2374
2375 trafCOD.fireTrafCODEvent(TrafficController.TRAFFICCONTROL_TRACED_VARIABLE_UPDATED, new Object[] {trafCOD.getId(),
2376 toString(EnumSet.of(PrintFlags.ID)), this.stream, this.value, newValue, cause.toString()});
2377 }
2378 this.value = newValue;
2379 return result;
2380 }
2381
2382
2383
2384
2385
2386
2387
2388 public void cloneState(final Variable fromVariable, final Network newNetwork) throws NetworkException
2389 {
2390 this.value = fromVariable.value;
2391 this.flags = EnumSet.copyOf(fromVariable.flags);
2392 this.updateTime10 = fromVariable.updateTime10;
2393 if (fromVariable.isOutput())
2394 {
2395 for (TrafficLight tl : fromVariable.trafficLights)
2396 {
2397 ObjectInterface clonedTrafficLight = newNetwork.getObjectMap().get(tl.getId());
2398 if (null != clonedTrafficLight)
2399 {
2400 throw new NetworkException("newNetwork does not contain a clone of traffic light " + tl.getId());
2401 }
2402 if (clonedTrafficLight instanceof TrafficLight)
2403 {
2404 throw new NetworkException(
2405 "newNetwork contains an object with name " + tl.getId() + " but this object is not a TrafficLight");
2406 }
2407 this.trafficLights.add((TrafficLight) clonedTrafficLight);
2408 }
2409 }
2410 if (isOutput())
2411 {
2412 for (TrafficLight trafficLight : this.trafficLights)
2413 {
2414 trafficLight.setTrafficLightColor(this.color);
2415 }
2416 }
2417 }
2418
2419
2420
2421
2422
2423
2424 public int getTimerMax() throws TrafficControlException
2425 {
2426 if (!this.isTimer())
2427 {
2428 throw new TrafficControlException("This is not a timer");
2429 }
2430 return this.timerMax10;
2431 }
2432
2433
2434
2435
2436
2437 public int getValue()
2438 {
2439 return this.value;
2440 }
2441
2442
2443
2444
2445
2446 public void setFlag(final Flags flag)
2447 {
2448 this.flags.add(flag);
2449 }
2450
2451
2452
2453
2454
2455 public void clearFlag(final Flags flag)
2456 {
2457 this.flags.remove(flag);
2458 }
2459
2460
2461
2462
2463
2464 public boolean isTimer()
2465 {
2466 return this.flags.contains(Flags.IS_TIMER);
2467 }
2468
2469
2470
2471
2472 public void clearChangedFlag()
2473 {
2474 this.flags.remove(Flags.CHANGED);
2475 }
2476
2477
2478
2479
2480
2481 public void incrementReferenceCount()
2482 {
2483 this.refCount++;
2484 }
2485
2486
2487
2488
2489
2490 public EnumSet<Flags> getFlags()
2491 {
2492 return EnumSet.copyOf(this.flags);
2493 }
2494
2495
2496
2497
2498
2499
2500 public void setOutput(int colorValue) throws TrafficControlException
2501 {
2502 if (null != this.color)
2503 {
2504 throw new TrafficControlException("setOutput has already been called for " + this);
2505 }
2506 if (null == this.trafficLights)
2507 {
2508 this.trafficLights = new LinkedHashSet<>();
2509 }
2510
2511 TrafficLightColor newColor;
2512 switch (colorValue)
2513 {
2514 case 'R':
2515 newColor = TrafficLightColor.RED;
2516 break;
2517 case 'G':
2518 newColor = TrafficLightColor.GREEN;
2519 break;
2520 case 'Y':
2521 newColor = TrafficLightColor.YELLOW;
2522 break;
2523 default:
2524 throw new TrafficControlException("Bad color value: " + colorValue);
2525 }
2526 this.color = newColor;
2527 this.flags.add(Flags.IS_OUTPUT);
2528 }
2529
2530
2531
2532
2533
2534
2535 public void addOutput(final TrafficLight trafficLight) throws TrafficControlException
2536 {
2537 if (!this.isOutput())
2538 {
2539 throw new TrafficControlException("Cannot add an output to an non-output variable");
2540 }
2541 this.trafficLights.add(trafficLight);
2542 }
2543
2544
2545
2546
2547
2548
2549 public void setTimerMax(int value10) throws TrafficControlException
2550 {
2551 if (!this.flags.contains(Flags.IS_TIMER))
2552 {
2553 throw new TrafficControlException(
2554 "Cannot set maximum timer value of " + this.toString() + " because this is not a timer");
2555 }
2556 this.timerMax10 = value10;
2557 }
2558
2559
2560
2561
2562
2563 public String getStartSource()
2564 {
2565 return this.startSource;
2566 }
2567
2568
2569
2570
2571
2572
2573 public void setStartSource(String startSource) throws TrafficControlException
2574 {
2575 if (null != this.startSource)
2576 {
2577 throw new TrafficControlException("Conflicting rules: " + this.startSource + " vs " + startSource);
2578 }
2579 this.startSource = startSource;
2580 this.flags.add(Flags.HAS_START_RULE);
2581 }
2582
2583
2584
2585
2586
2587 public String getEndSource()
2588 {
2589 return this.endSource;
2590 }
2591
2592
2593
2594
2595
2596
2597 public void setEndSource(String endSource) throws TrafficControlException
2598 {
2599 if (null != this.endSource)
2600 {
2601 throw new TrafficControlException("Conflicting rules: " + this.startSource + " vs " + endSource);
2602 }
2603 this.endSource = endSource;
2604 this.flags.add(Flags.HAS_END_RULE);
2605 }
2606
2607
2608
2609
2610
2611 public short getStream()
2612 {
2613 return this.stream;
2614 }
2615
2616
2617 @Override
2618 public String toString()
2619 {
2620 return "Variable [" + toString(EnumSet.of(PrintFlags.ID, PrintFlags.VALUE, PrintFlags.FLAGS)) + "]";
2621 }
2622
2623
2624
2625
2626
2627
2628 public String toString(EnumSet<PrintFlags> printFlags)
2629 {
2630 StringBuilder result = new StringBuilder();
2631 if (printFlags.contains(PrintFlags.ID))
2632 {
2633 if (this.flags.contains(Flags.IS_DETECTOR))
2634 {
2635 result.append("D");
2636 }
2637 else if (isTimer() && printFlags.contains(PrintFlags.INITTIMER))
2638 {
2639 result.append("I");
2640 result.append(this.name);
2641 }
2642 else if (isTimer() && printFlags.contains(PrintFlags.REINITTIMER))
2643 {
2644 result.append("RI");
2645 result.append(this.name);
2646 }
2647 else
2648 {
2649 result.append(this.name);
2650 }
2651 if (this.stream > 0)
2652 {
2653
2654 int pos;
2655 for (pos = 0; pos < result.length(); pos++)
2656 {
2657 if (Character.isDigit(result.charAt(pos)))
2658 {
2659 break;
2660 }
2661 }
2662 result.insert(pos, String.format("%02d", this.stream));
2663 }
2664 if (this.flags.contains(Flags.IS_DETECTOR))
2665 {
2666 result.append(this.name.substring(1));
2667 }
2668 if (printFlags.contains(PrintFlags.NEGATED))
2669 {
2670 result.append("N");
2671 }
2672 }
2673 int printValue = Integer.MIN_VALUE;
2674 if (printFlags.contains(PrintFlags.VALUE))
2675 {
2676 if (printFlags.contains(PrintFlags.NEGATED))
2677 {
2678 printValue = 0 == this.value ? 1 : 0;
2679 }
2680 else
2681 {
2682 printValue = this.value;
2683 }
2684 if (printFlags.contains(PrintFlags.S))
2685 {
2686 if (this.flags.contains(Flags.START))
2687 {
2688 printValue = 1;
2689 }
2690 else
2691 {
2692 printValue = 0;
2693 }
2694 }
2695 if (printFlags.contains(PrintFlags.E))
2696 {
2697 if (this.flags.contains(Flags.END))
2698 {
2699 printValue = 1;
2700 }
2701 else
2702 {
2703 printValue = 0;
2704 }
2705 }
2706 }
2707 if (printFlags.contains(PrintFlags.VALUE) || printFlags.contains(PrintFlags.S) || printFlags.contains(PrintFlags.E)
2708 || printFlags.contains(PrintFlags.FLAGS))
2709 {
2710 result.append("<");
2711 if (printFlags.contains(PrintFlags.VALUE) || printFlags.contains(PrintFlags.S) || printFlags.contains(PrintFlags.E))
2712 {
2713 result.append(printValue);
2714 }
2715 if (printFlags.contains(PrintFlags.FLAGS))
2716 {
2717 if (this.flags.contains(Flags.START))
2718 {
2719 result.append("S");
2720 }
2721 if (this.flags.contains(Flags.END))
2722 {
2723 result.append("E");
2724 }
2725 }
2726 result.append(">");
2727 }
2728 if (printFlags.contains(PrintFlags.MODIFY_TIME))
2729 {
2730 result.append(String.format(" (%d.%d)", this.updateTime10 / 10, this.updateTime10 % 10));
2731 }
2732 return result.toString();
2733 }
2734
2735
2736 @Override
2737 public void notify(EventInterface event) throws RemoteException
2738 {
2739 if (event.getType().equals(NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_ENTRY_EVENT))
2740 {
2741 setValue(1, this.updateTime10, new CausePrinter("Detector became occupied"), this.trafCOD);
2742 }
2743 else if (event.getType().equals(NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_EXIT_EVENT))
2744 {
2745 setValue(0, this.updateTime10, new CausePrinter("Detector became unoccupied"), this.trafCOD);
2746 }
2747 }
2748
2749 }
2750
2751
2752
2753
2754
2755 class CausePrinter
2756 {
2757
2758 final Object cause;
2759
2760
2761
2762
2763
2764 public CausePrinter(final Object cause)
2765 {
2766 this.cause = cause;
2767 }
2768
2769 @Override
2770 public String toString()
2771 {
2772 if (this.cause instanceof String)
2773 {
2774 return (String) this.cause;
2775 }
2776 else if (this.cause instanceof Object[])
2777 {
2778 try
2779 {
2780 return TrafCOD.printRule((Object[]) this.cause, true);
2781 }
2782 catch (TrafficControlException exception)
2783 {
2784 exception.printStackTrace();
2785 return ("printRule failed");
2786 }
2787 }
2788 return this.cause.toString();
2789 }
2790 }
2791
2792
2793
2794
2795 enum PrintFlags
2796 {
2797
2798 ID,
2799
2800 VALUE,
2801
2802 INITTIMER,
2803
2804 REINITTIMER,
2805
2806 S,
2807
2808 E,
2809
2810 NEGATED,
2811
2812 FLAGS,
2813
2814 MODIFY_TIME,
2815 }
2816
2817
2818
2819
2820 enum Flags
2821 {
2822
2823 START,
2824
2825 END,
2826
2827 TIMEREXPIRED,
2828
2829 CHANGED,
2830
2831 IS_TIMER,
2832
2833 IS_DETECTOR,
2834
2835 HAS_START_RULE,
2836
2837 HAS_END_RULE,
2838
2839 IS_OUTPUT,
2840
2841 INITED,
2842
2843 TRACED,
2844
2845 CONFLICT_GROUP,
2846 }