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