1 package org.opentrafficsim.trafficcontrol.trafcod;
2
3 import java.io.BufferedReader;
4 import java.io.InputStreamReader;
5 import java.net.URL;
6 import java.rmi.RemoteException;
7 import java.util.ArrayList;
8 import java.util.EnumSet;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.Set;
15
16 import nl.tudelft.simulation.dsol.SimRuntimeException;
17 import nl.tudelft.simulation.dsol.simulators.DEVSSimulator;
18 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
19 import nl.tudelft.simulation.event.EventProducer;
20 import nl.tudelft.simulation.event.EventType;
21 import nl.tudelft.simulation.language.Throw;
22
23 import org.djunits.unit.LengthUnit;
24 import org.djunits.unit.SpeedUnit;
25 import org.djunits.unit.TimeUnit;
26 import org.djunits.value.formatter.EngineeringFormatter;
27 import org.djunits.value.vdouble.scalar.Duration;
28 import org.djunits.value.vdouble.scalar.Length;
29 import org.djunits.value.vdouble.scalar.Speed;
30 import org.djunits.value.vdouble.scalar.Time;
31 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
32 import org.opentrafficsim.core.dsol.OTSModelInterface;
33 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
34 import org.opentrafficsim.core.geometry.OTSPoint3D;
35 import org.opentrafficsim.core.gtu.GTUType;
36 import org.opentrafficsim.core.network.Link;
37 import org.opentrafficsim.core.network.LinkType;
38 import org.opentrafficsim.core.network.LongitudinalDirectionality;
39 import org.opentrafficsim.core.network.Network;
40 import org.opentrafficsim.core.network.Node;
41 import org.opentrafficsim.core.network.OTSLink;
42 import org.opentrafficsim.core.network.OTSNetwork;
43 import org.opentrafficsim.core.network.OTSNode;
44 import org.opentrafficsim.road.network.lane.CrossSectionLink;
45 import org.opentrafficsim.road.network.lane.Lane;
46 import org.opentrafficsim.road.network.lane.LaneType;
47 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
48 import org.opentrafficsim.road.network.lane.object.sensor.Sensor;
49 import org.opentrafficsim.road.network.lane.object.sensor.TrafficLightSensor;
50 import org.opentrafficsim.road.network.lane.object.trafficlight.SimpleTrafficLight;
51 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
52 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
53 import org.opentrafficsim.simulationengine.SimpleSimulator;
54 import org.opentrafficsim.trafficcontrol.TrafficController;
55
56
57
58
59
60
61
62
63
64
65 public class TrafCOD extends EventProducer implements TrafficController
66 {
67
68 private static final long serialVersionUID = 20161014L;
69
70
71 final String controllerName;
72
73
74 final static int TRAFCOD_VERSION = 100;
75
76
77 final static Duration EVALUATION_INTERVAL = new Duration(0.1, TimeUnit.SECOND);
78
79
80 private final static String VERSION_PREFIX = "trafcod-version=";
81
82
83 private final static String SEQUENCE_KEY = "Sequence";
84
85
86 private final static String STRUCTURE_PREFIX = "Structure:";
87
88
89 final List<String> trafcodRules = new ArrayList<>();
90
91
92 final List<Object[]> tokenisedRules = new ArrayList<>();
93
94
95 final Map<String, Variable> variables = new HashMap<>();
96
97
98 final Map<String, Variable> detectors = new HashMap<>();
99
100
101 final static String COMMENT_START = "#";
102
103
104 private final static String INIT_PREFIX = "%init ";
105
106
107 private final static String TIME_PREFIX = "%time ";
108
109
110 private final static String EXPORT_PREFIX = "%export ";
111
112
113 private int conflictGroups = -1;
114
115
116 private int conflictGroupSize = -1;
117
118
119 private int structureNumber = -1;
120
121
122 private List<List<Short>> conflictgroups = new ArrayList<>();
123
124
125 private int maxLoopCount = 10;
126
127
128 private int currentToken;
129
130
131 private List<Integer> stack = new ArrayList<Integer>();
132
133
134 private Object[] currentRule;
135
136
137 private int currentTime10 = 0;
138
139
140 public static final EventType TRAFFIC_LIGHT_CHANGED = new EventType("TrafficLightChanged");
141
142
143 private final DEVSSimulator<Time, Duration, OTSSimTimeDouble> simulator;
144
145
146
147
148
149
150
151
152
153
154
155 public TrafCOD(String controllerName, final String trafCodURL, final Set<TrafficLight> trafficLights,
156 final Set<Sensor> sensors, final DEVSSimulator<Time, Duration, OTSSimTimeDouble> simulator) throws Exception
157 {
158 Throw.whenNull(trafCodURL, "trafCodURL may not be null");
159 Throw.whenNull(controllerName, "controllerName may not be null");
160 Throw.whenNull(trafficLights, "trafficLights may not be null");
161 Throw.whenNull(simulator, "simulator may not be null");
162 this.simulator = simulator;
163 this.controllerName = controllerName;
164 BufferedReader in = new BufferedReader(new InputStreamReader(new URL(trafCodURL).openStream()));
165 String inputLine;
166 int lineno = 0;
167 while ((inputLine = in.readLine()) != null)
168 {
169 ++lineno;
170 System.out.println(lineno + ":\t" + inputLine);
171 String trimmedLine = inputLine.trim();
172 if (trimmedLine.length() == 0)
173 {
174 continue;
175 }
176 String locationDescription = trafCodURL + "(" + lineno + ") ";
177 if (trimmedLine.startsWith(COMMENT_START))
178 {
179 String commentStripped = trimmedLine.substring(1).trim();
180 if (stringBeginsWithIgnoreCase(VERSION_PREFIX, commentStripped))
181 {
182 String versionString = commentStripped.substring(VERSION_PREFIX.length());
183 try
184 {
185 int observedVersion = Integer.parseInt(versionString);
186 if (TRAFCOD_VERSION != observedVersion)
187 {
188 throw new Exception("Wrong TrafCOD version (expected " + TRAFCOD_VERSION + ", got "
189 + observedVersion + ")");
190 }
191 }
192 catch (NumberFormatException nfe)
193 {
194 nfe.printStackTrace();
195 throw new Exception("Could not parse TrafCOD version (got \"" + versionString + ")");
196 }
197 }
198 else if (stringBeginsWithIgnoreCase(SEQUENCE_KEY, commentStripped))
199 {
200 while (trimmedLine.startsWith(COMMENT_START))
201 {
202 inputLine = in.readLine();
203 if (null == inputLine)
204 {
205 throw new Exception("Unexpected EOF (reading sequence key at " + locationDescription + ")");
206 }
207 ++lineno;
208 trimmedLine = inputLine.trim();
209 }
210 String[] fields = inputLine.split("\t");
211 if (fields.length != 2)
212 {
213 throw new Exception("Wrong number of fields in Sequence information");
214 }
215 try
216 {
217 this.conflictGroups = Integer.parseInt(fields[0]);
218 this.conflictGroupSize = Integer.parseInt(fields[1]);
219 }
220 catch (NumberFormatException nfe)
221 {
222 nfe.printStackTrace();
223 throw new Exception("Bad number of conflict groups or bad conflict group size");
224 }
225 }
226 else if (stringBeginsWithIgnoreCase(STRUCTURE_PREFIX, commentStripped))
227 {
228 String structureNumberString = commentStripped.substring(STRUCTURE_PREFIX.length()).trim();
229 try
230 {
231 this.setStructureNumber(Integer.parseInt(structureNumberString));
232 }
233 catch (NumberFormatException nfe)
234 {
235 nfe.printStackTrace();
236 throw new Exception("Bad structure number (got \"" + structureNumberString + "\" at "
237 + locationDescription + ")");
238 }
239 for (int conflictMemberLine = 0; conflictMemberLine < this.conflictGroups; conflictMemberLine++)
240 {
241 while (trimmedLine.startsWith(COMMENT_START))
242 {
243 inputLine = in.readLine();
244 if (null == inputLine)
245 {
246 throw new Exception("Unexpected EOF (reading sequence key at " + locationDescription + ")");
247 }
248 ++lineno;
249 trimmedLine = inputLine.trim();
250 }
251 String[] fields = inputLine.split("\t");
252 if (fields.length != this.conflictGroupSize)
253 {
254 throw new Exception("Wrong number of conflict groups in Structure information");
255 }
256 List<Short> row = new ArrayList<>(this.conflictGroupSize);
257 for (int col = 0; col < this.conflictGroupSize; col++)
258 {
259 try
260 {
261 Short stream = Short.parseShort(fields[col]);
262 row.add(stream);
263 }
264 catch (NumberFormatException nfe)
265 {
266 nfe.printStackTrace();
267 throw new Exception("Wrong number of streams in conflict group " + trimmedLine);
268 }
269 }
270 }
271 }
272 continue;
273 }
274 if (stringBeginsWithIgnoreCase(INIT_PREFIX, trimmedLine))
275 {
276 String varNameAndInitialValue = trimmedLine.substring(INIT_PREFIX.length()).trim().replaceAll("[ \t]+", " ");
277 String[] fields = varNameAndInitialValue.split(" ");
278 NameAndStream nameAndStream = new NameAndStream(fields[0], locationDescription);
279 installVariable(nameAndStream.getName(), nameAndStream.getStream(), EnumSet.noneOf(Flags.class),
280 locationDescription);
281
282 continue;
283 }
284 if (stringBeginsWithIgnoreCase(TIME_PREFIX, trimmedLine))
285 {
286 String timerNameAndMaximumValue = trimmedLine.substring(INIT_PREFIX.length()).trim().replaceAll("[ \t]+", " ");
287 String[] fields = timerNameAndMaximumValue.split(" ");
288 NameAndStream nameAndStream = new NameAndStream(fields[0], locationDescription);
289 Variable variable =
290 installVariable(nameAndStream.getName(), nameAndStream.getStream(), EnumSet.noneOf(Flags.class),
291 locationDescription);
292 int value10 = Integer.parseInt(fields[1]);
293 variable.setTimerMax(value10);
294 continue;
295 }
296 if (stringBeginsWithIgnoreCase(EXPORT_PREFIX, trimmedLine))
297 {
298 String varNameAndOutputValue = trimmedLine.substring(EXPORT_PREFIX.length()).trim().replaceAll("[ \t]+", " ");
299 String[] fields = varNameAndOutputValue.split(" ");
300 NameAndStream nameAndStream = new NameAndStream(fields[0], locationDescription);
301 Variable variable =
302 installVariable(nameAndStream.getName(), nameAndStream.getStream(), EnumSet.noneOf(Flags.class),
303 locationDescription);
304 int value = Integer.parseInt(fields[1]);
305
306 Set<TrafficLight> trafficLightsOfStream = new HashSet<>();
307 for (TrafficLight trafficLight : trafficLights)
308 {
309 String id = trafficLight.getId();
310 if (id.length() < 2)
311 {
312 throw new Exception("Id of traffic light " + trafficLight + " does not end on two digits");
313 }
314 String streamLetters = id.substring(id.length() - 2);
315 if (!Character.isDigit(streamLetters.charAt(0)) || !Character.isDigit(streamLetters.charAt(1)))
316 {
317 throw new Exception("Id of traffic light " + trafficLight + " does not end on two digits");
318 }
319 int stream = Integer.parseInt(streamLetters);
320 if (variable.getStream() == stream)
321 {
322 trafficLightsOfStream.add(trafficLight);
323 }
324 }
325 if (trafficLightsOfStream.size() == 0)
326 {
327 throw new Exception("No traffic light provided that matches stream " + variable.getStream());
328 }
329 variable.setOutput(value, trafficLightsOfStream);
330 continue;
331 }
332 this.trafcodRules.add(trimmedLine);
333 Object[] tokenisedRule = parse(trimmedLine, locationDescription);
334 if (null != tokenisedRule)
335 {
336 this.tokenisedRules.add(tokenisedRule);
337
338
339
340
341
342 String untokenised = printRule(tokenisedRule, false);
343 System.out.println(untokenised);
344 }
345 }
346 in.close();
347 for (Variable variable : this.variables.values())
348 {
349 if (variable.isDetector())
350 {
351 String detectorName = variable.selectedFieldsToString(EnumSet.of(PrintFlags.ID));
352 int detectorNumber = variable.getStream() * 10 + detectorName.charAt(detectorName.length() - 1) - '0';
353
354 }
355 }
356 System.out.println("Installed " + this.variables.size() + " variables");
357 for (String key : this.variables.keySet())
358 {
359 System.out.println(key
360 + ":\t"
361 + this.variables.get(key).selectedFieldsToString(
362 EnumSet.of(PrintFlags.ID, PrintFlags.VALUE, PrintFlags.INITTIMER, PrintFlags.REINITTIMER,
363 PrintFlags.S, PrintFlags.E)));
364 }
365 this.simulator.scheduleEventNow(this, this, "evalExprs", null);
366 }
367
368
369
370
371
372 @SuppressWarnings("unused")
373 private void evalExprs() throws Exception
374 {
375 System.out.println("TrafCOD: time is " + EngineeringFormatter.format(this.simulator.getSimulatorTime().get().si));
376 for (int loop = 0; loop < this.maxLoopCount; loop++)
377 {
378 if (evalExpressionsOnce() == 0)
379 {
380 break;
381 }
382 }
383 this.simulator.scheduleEventRel(EVALUATION_INTERVAL, this, this, "evalExprs", null);
384 }
385
386
387
388
389
390
391 private int evalExpressionsOnce() throws Exception
392 {
393 for (Variable variable : this.variables.values())
394 {
395 variable.clearChangedFlag();
396 }
397 int changeCount = 0;
398 for (Object[] rule : this.tokenisedRules)
399 {
400 System.out.println("Evaluating rule " + printRule(rule, true));
401 for (Object o : rule)
402 {
403 System.out.print(o + " ");
404 }
405 System.out.println("");
406
407 if (evalRule(rule))
408 {
409 changeCount++;
410 }
411 }
412 return changeCount;
413 }
414
415
416
417
418
419
420
421 private boolean evalRule(final Object[] rule) throws Exception
422 {
423 boolean result = false;
424 Token ruleType = (Token) rule[0];
425 Variable destination = (Variable) rule[1];
426 if (destination.isTimer())
427 {
428 if (destination.getFlags().contains(Flags.TIMEREXPIRED))
429 {
430 destination.clearFlag(Flags.TIMEREXPIRED);
431 destination.setFlag(Flags.END);
432 }
433 else if (destination.getFlags().contains(Flags.START) || destination.getFlags().contains(Flags.END))
434 {
435 destination.clearFlag(Flags.START);
436 destination.clearFlag(Flags.END);
437 destination.setFlag(Flags.CHANGED);
438 }
439 }
440 else
441 {
442
443 if (Token.START_RULE == ruleType)
444 {
445 destination.clearFlag(Flags.START);
446 }
447 else if (Token.END_RULE == ruleType)
448 {
449 destination.clearFlag(Flags.END);
450 }
451 else
452 {
453 destination.clearFlag(Flags.START);
454 destination.clearFlag(Flags.END);
455 }
456 }
457
458 int currentValue = destination.getValue();
459 if (Token.START_RULE == ruleType && currentValue != 0 || Token.END == ruleType && currentValue == 0
460 || Token.INIT_TIMER == ruleType && currentValue != 0)
461 {
462 return false;
463 }
464 this.currentRule = rule;
465 this.currentToken = 2;
466 this.stack.clear();
467 evalExpr(0);
468 if (this.currentToken < this.currentRule.length && Token.CLOSE_PAREN == this.currentRule[this.currentToken])
469 {
470 throw new Exception("Too man closing parentheses");
471 }
472 int resultValue = pop();
473 if (Token.END_RULE == ruleType)
474 {
475
476 if (0 == resultValue)
477 {
478 resultValue = destination.getValue();
479 }
480 else
481 {
482 resultValue = 0;
483 }
484 }
485 if (destination.isTimer())
486 {
487 if (resultValue != 0 && Token.END_RULE == ruleType)
488 {
489 if (destination.getValue() == 0)
490 {
491 result = true;
492 }
493
494 int timerValue10 = destination.getTimerMax();
495 if (timerValue10 < 1)
496 {
497
498 timerValue10 = 1;
499 }
500 destination.setValue(timerValue10, this.currentTime10);
501 }
502 else if (0 == resultValue && Token.END_RULE == ruleType && destination.getValue() != 0)
503 {
504 result = true;
505 destination.setValue(0, this.currentTime10);
506 }
507
508 }
509 else if (destination.getValue() != resultValue)
510 {
511 result = true;
512 destination.setValue(resultValue, this.currentTime10);
513 if (destination.isOutput())
514 {
515 fireEvent(TRAFFIC_LIGHT_CHANGED,
516 new Object[] { this.controllerName, destination.getStartSource(), destination.getColor() });
517 }
518
519 }
520
521 return result;
522 }
523
524
525 private static final int BIND_RELATIONAL_OPERATOR = 1;
526
527
528 private static final int BIND_ADDITION = 2;
529
530
531 private static final int BIND_MULTIPLY = 3;
532
533
534 private static int BIND_UNARY_MINUS = 4;
535
536
537
538
539
540
541
542
543
544
545
546
547 private void evalExpr(final int bindingStrength) throws Exception
548 {
549 if (this.currentToken >= this.currentRule.length)
550 {
551 throw new Exception("Missing operand at end of expression " + printRule(this.currentRule, false));
552 }
553 Token token = (Token) this.currentRule[this.currentToken++];
554 Object nextToken = null;
555 if (this.currentToken < this.currentRule.length)
556 {
557 nextToken = this.currentRule[this.currentToken];
558 }
559 switch (token)
560 {
561 case UNARY_MINUS:
562 if (Token.OPEN_PAREN != nextToken && Token.VARIABLE != nextToken && Token.NEG_VARIABLE != nextToken
563 && Token.CONSTANT != nextToken && Token.START != nextToken && Token.END != nextToken)
564 {
565 throw new Exception("Operand expected after unary minus");
566 }
567 evalExpr(BIND_UNARY_MINUS);
568 push(-pop());
569 break;
570
571 case OPEN_PAREN:
572 evalExpr(0);
573 if (Token.CLOSE_PAREN != this.currentRule[this.currentToken])
574 {
575 throw new Exception("Missing closing parenthesis");
576 }
577 this.currentToken++;
578 break;
579
580 case START:
581 if (Token.VARIABLE != nextToken || this.currentToken >= this.currentRule.length - 1)
582 {
583 throw new Exception("Missing variable after S");
584 }
585 nextToken = this.currentRule[++this.currentToken];
586 if (!(nextToken instanceof Variable))
587 {
588 throw new Exception("Missing variable after S");
589 }
590 push(((Variable) nextToken).getFlags().contains(Flags.START) ? 1 : 0);
591 this.currentToken++;
592 break;
593
594 case END:
595 if (Token.VARIABLE != nextToken || this.currentToken >= this.currentRule.length - 1)
596 {
597 throw new Exception("Missing variable after E");
598 }
599 nextToken = this.currentRule[++this.currentToken];
600 if (!(nextToken instanceof Variable))
601 {
602 throw new Exception("Missing variable after E");
603 }
604 push(((Variable) nextToken).getFlags().contains(Flags.END) ? 1 : 0);
605 this.currentToken++;
606 break;
607
608 case VARIABLE:
609 {
610 Variable operand = (Variable) nextToken;
611 if (operand.isTimer())
612 {
613 push(operand.getValue() == 0 ? 0 : 1);
614 }
615 else
616 {
617 push(operand.getValue());
618 }
619 this.currentToken++;
620 break;
621 }
622
623 case CONSTANT:
624 push((Integer) nextToken);
625 this.currentToken++;
626 break;
627
628 case NEG_VARIABLE:
629 push(-((Variable) nextToken).getValue());
630 this.currentToken++;
631 break;
632
633 default:
634 throw new Exception("Operand missing");
635 }
636 evalRHS(bindingStrength);
637 }
638
639
640
641
642
643
644 private void evalRHS(final int bindingStrength) throws Exception
645 {
646 while (true)
647 {
648 if (this.currentToken >= this.currentRule.length)
649 {
650 return;
651 }
652 Token token = (Token) this.currentRule[this.currentToken];
653 switch (token)
654 {
655 case CLOSE_PAREN:
656 return;
657
658 case TIMES:
659 if (BIND_MULTIPLY <= bindingStrength)
660 {
661 return;
662 }
663
664
665
666
667
668
669 this.currentToken++;
670 evalExpr(BIND_MULTIPLY);
671 push(pop() * pop() == 0 ? 0 : 1);
672 break;
673
674 case EQ:
675 case NOTEQ:
676 case LE:
677 case LEEQ:
678 case GT:
679 case GTEQ:
680 if (BIND_RELATIONAL_OPERATOR <= bindingStrength)
681 {
682 return;
683 }
684
685
686
687
688
689
690 this.currentToken++;
691 evalExpr(BIND_RELATIONAL_OPERATOR);
692 switch (token)
693 {
694 case EQ:
695 push(pop() == pop() ? 1 : 0);
696 break;
697
698 case NOTEQ:
699 push(pop() != pop() ? 1 : 0);
700 break;
701
702 case GT:
703 push(pop() < pop() ? 1 : 0);
704 break;
705
706 case GTEQ:
707 push(pop() <= pop() ? 1 : 0);
708 break;
709
710 case LE:
711 push(pop() > pop() ? 1 : 0);
712 break;
713
714 case LEEQ:
715 push(pop() >= pop() ? 1 : 0);
716 break;
717
718 default:
719 throw new Exception("Bad relational operator");
720 }
721 break;
722
723 case PLUS:
724 if (BIND_ADDITION <= bindingStrength)
725 {
726 return;
727 }
728
729
730
731
732
733
734 this.currentToken++;
735 evalExpr(BIND_ADDITION);
736 push(pop() + pop() == 0 ? 0 : 1);
737 break;
738
739 case MINUS:
740 if (BIND_ADDITION <= bindingStrength)
741 {
742 return;
743 }
744
745
746
747
748
749
750 this.currentToken++;
751 evalExpr(BIND_ADDITION);
752 push(-pop() + pop());
753 break;
754
755 default:
756 throw new Exception("Missing binary operator");
757 }
758 }
759 }
760
761
762
763
764
765 private void push(final int value)
766 {
767 this.stack.add(value);
768 }
769
770
771
772
773
774
775 private int pop() throws Exception
776 {
777 if (this.stack.size() < 1)
778 {
779 throw new Exception("Stack empty");
780 }
781 return this.stack.remove(this.stack.size() - 1);
782 }
783
784
785
786
787
788
789
790
791
792 private String printRule(Object[] tokens, final boolean printValues) throws Exception
793 {
794 StringBuilder result = new StringBuilder();
795 for (int inPos = 0; inPos < tokens.length; inPos++)
796 {
797 Object token = tokens[inPos];
798 if (token instanceof Token)
799 {
800 switch ((Token) token)
801 {
802 case EQUALS_RULE:
803 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID)));
804 result.append("=");
805 break;
806 case NEG_EQUALS_RULE:
807 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID,
808 PrintFlags.NEGATED)));
809 result.append("=");
810 break;
811 case START_RULE:
812 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID)));
813 result.append(".=");
814 break;
815 case END_RULE:
816 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID,
817 PrintFlags.NEGATED)));
818 result.append(".=");
819 break;
820 case INIT_TIMER:
821 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID,
822 PrintFlags.INITTIMER)));
823 result.append(".=");
824 break;
825 case REINIT_TIMER:
826 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID,
827 PrintFlags.REINITTIMER)));
828 result.append(".=");
829 break;
830 case START:
831 result.append("S");
832 break;
833 case END:
834 result.append("E");
835 break;
836 case VARIABLE:
837 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID)));
838 break;
839 case NEG_VARIABLE:
840 result.append(((Variable) tokens[++inPos]).selectedFieldsToString(EnumSet.of(PrintFlags.ID,
841 PrintFlags.NEGATED)));
842 break;
843 case CONSTANT:
844 result.append(tokens[++inPos]).toString();
845 break;
846 case UNARY_MINUS:
847 case MINUS:
848 result.append("-");
849 break;
850 case PLUS:
851 result.append("+");
852 break;
853 case TIMES:
854 result.append(".");
855 break;
856 case EQ:
857 result.append("=");
858 break;
859 case NOTEQ:
860 result.append("<>");
861 break;
862 case GT:
863 result.append(">");
864 break;
865 case GTEQ:
866 result.append(">=");
867 break;
868 case LE:
869 result.append("<");
870 break;
871 case LEEQ:
872 result.append("<=");
873 break;
874 case OPEN_PAREN:
875 result.append("(");
876 break;
877 case CLOSE_PAREN:
878 result.append(")");
879 break;
880 default:
881 System.out.println("<<<ERROR>>> encountered a non-Token object: " + token + " after "
882 + result.toString());
883 throw new Exception("Unknown token");
884
885 }
886 }
887 else
888 {
889 System.out.println("<<<ERROR>>> encountered a non-Token object: " + token + " after " + result.toString());
890 throw new Exception("Not a token");
891 }
892 }
893 return result.toString();
894 }
895
896
897
898
899
900
901 enum ParserState
902 {
903
904 FIND_LHS,
905
906 FIND_ASSIGN,
907
908 FIND_RHS,
909
910 MAY_UMINUS,
911
912 FIND_EXPR,
913 }
914
915
916
917
918
919
920 enum Token
921 {
922
923 EQUALS_RULE,
924
925 NEG_EQUALS_RULE,
926
927 ASSIGNMENT,
928
929 START_RULE,
930
931 END_RULE,
932
933 INIT_TIMER,
934
935 REINIT_TIMER,
936
937 UNARY_MINUS,
938
939 LEEQ,
940
941 NOTEQ,
942
943 LE,
944
945 GTEQ,
946
947 GT,
948
949 EQ,
950
951 START,
952
953 END,
954
955 VARIABLE,
956
957 NEG_VARIABLE,
958
959 CONSTANT,
960
961 PLUS,
962
963 MINUS,
964
965 TIMES,
966
967 OPEN_PAREN,
968
969 CLOSE_PAREN,
970 }
971
972
973
974
975
976
977
978
979 private Object[] parse(final String rawRule, final String locationDescription) throws Exception
980 {
981 if (rawRule.length() == 0)
982 {
983 throw new Exception("empty rule at " + locationDescription);
984 }
985 ParserState state = ParserState.FIND_LHS;
986 String rule = rawRule.toUpperCase(Locale.US);
987 Token ruleType = Token.ASSIGNMENT;
988 int inPos = 0;
989 NameAndStream lhsNameAndStream = null;
990 List<Object> tokens = new ArrayList<>();
991 while (inPos < rule.length())
992 {
993 char character = rule.charAt(inPos);
994 if (Character.isWhitespace(character))
995 {
996 inPos++;
997 continue;
998 }
999 switch (state)
1000 {
1001 case FIND_LHS:
1002 {
1003 if ('S' == character)
1004 {
1005 ruleType = Token.START_RULE;
1006 inPos++;
1007 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1008 inPos += lhsNameAndStream.getNumberOfChars();
1009 }
1010 else if ('E' == character)
1011 {
1012 ruleType = Token.END_RULE;
1013 inPos++;
1014 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1015 inPos += lhsNameAndStream.getNumberOfChars();
1016 }
1017 else if ('I' == character && 'T' == rule.charAt(inPos + 1))
1018 {
1019 ruleType = Token.INIT_TIMER;
1020 inPos++;
1021 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1022 inPos += lhsNameAndStream.getNumberOfChars();
1023 }
1024 else if ('R' == character && 'I' == rule.charAt(inPos + 1) && 'T' == rule.charAt(inPos + 2))
1025 {
1026 ruleType = Token.REINIT_TIMER;
1027 inPos += 2;
1028 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1029 inPos += lhsNameAndStream.getNumberOfChars();
1030 }
1031 else if ('T' == character && rule.indexOf('=') >= 0
1032 && (rule.indexOf('N') < 0 || rule.indexOf('N') > rule.indexOf('=')))
1033 {
1034 throw new Exception("Bad time initialization at " + locationDescription);
1035 }
1036 else
1037 {
1038 ruleType = Token.EQUALS_RULE;
1039 lhsNameAndStream = new NameAndStream(rule.substring(inPos), locationDescription);
1040 inPos += lhsNameAndStream.getNumberOfChars();
1041 if (lhsNameAndStream.isNegated())
1042 {
1043 ruleType = Token.NEG_EQUALS_RULE;
1044 }
1045 }
1046 state = ParserState.FIND_ASSIGN;
1047 break;
1048 }
1049 case FIND_ASSIGN:
1050 {
1051 if ('.' == character && '=' == rule.charAt(inPos + 1))
1052 {
1053 if (Token.EQUALS_RULE == ruleType)
1054 {
1055 ruleType = Token.START_RULE;
1056 }
1057 else if (Token.NEG_EQUALS_RULE == ruleType)
1058 {
1059 ruleType = Token.END_RULE;
1060 }
1061 inPos += 2;
1062 }
1063 else if ('=' == character)
1064 {
1065 if (Token.START_RULE == ruleType || Token.END_RULE == ruleType || Token.INIT_TIMER == ruleType
1066 || Token.REINIT_TIMER == ruleType)
1067 {
1068 throw new Exception("Bad assignment at " + locationDescription);
1069 }
1070 inPos++;
1071 }
1072 tokens.add(ruleType);
1073 EnumSet<Flags> lhsFlags = EnumSet.noneOf(Flags.class);
1074 if (Token.START_RULE == ruleType || Token.EQUALS_RULE == ruleType || Token.NEG_EQUALS_RULE == ruleType
1075 || Token.INIT_TIMER == ruleType || Token.REINIT_TIMER == ruleType)
1076 {
1077 lhsFlags.add(Flags.HAS_START_RULE);
1078 }
1079 if (Token.END_RULE == ruleType || Token.EQUALS_RULE == ruleType || Token.NEG_EQUALS_RULE == ruleType)
1080 {
1081 lhsFlags.add(Flags.HAS_END_RULE);
1082 }
1083 Variable lhsVariable =
1084 installVariable(lhsNameAndStream.getName(), lhsNameAndStream.getStream(), lhsFlags,
1085 locationDescription);
1086 tokens.add(lhsVariable);
1087 state = ParserState.MAY_UMINUS;
1088 break;
1089 }
1090 case MAY_UMINUS:
1091 if ('-' == character)
1092 {
1093 tokens.add(Token.UNARY_MINUS);
1094 inPos++;
1095 }
1096 state = ParserState.FIND_EXPR;
1097 break;
1098
1099 case FIND_EXPR:
1100 {
1101 if (Character.isDigit(character))
1102 {
1103 int constValue = 0;
1104 while (inPos < rule.length() && Character.isDigit(rule.charAt(inPos)))
1105 {
1106 int digit = rule.charAt(inPos) - '0';
1107 if (constValue >= (Integer.MAX_VALUE - digit) / 10)
1108 {
1109 throw new Exception("Number too large at " + locationDescription);
1110 }
1111 constValue = 10 * constValue + digit;
1112 inPos++;
1113 }
1114 tokens.add(Token.CONSTANT);
1115 tokens.add(new Integer(constValue));
1116 }
1117 if (inPos >= rule.length())
1118 {
1119 return tokens.toArray();
1120 }
1121 character = rule.charAt(inPos);
1122 switch (character)
1123 {
1124 case '+':
1125 tokens.add(Token.PLUS);
1126 inPos++;
1127 break;
1128 case '-':
1129 tokens.add(Token.MINUS);
1130 inPos++;
1131 break;
1132 case '.':
1133 tokens.add(Token.TIMES);
1134 inPos++;
1135 break;
1136 case ')':
1137 tokens.add(Token.CLOSE_PAREN);
1138 inPos++;
1139 break;
1140
1141 case '<':
1142 {
1143 Character nextChar = rule.charAt(++inPos);
1144 if ('=' == nextChar)
1145 {
1146 tokens.add(Token.LEEQ);
1147 inPos++;
1148 }
1149 else if ('>' == nextChar)
1150 {
1151 tokens.add(Token.NOTEQ);
1152 inPos++;
1153 }
1154 else
1155 {
1156 tokens.add(Token.LE);
1157 }
1158 break;
1159 }
1160 case '>':
1161 {
1162 Character nextChar = rule.charAt(++inPos);
1163 if ('=' == nextChar)
1164 {
1165 tokens.add(Token.GTEQ);
1166 inPos++;
1167 }
1168 else if ('<' == nextChar)
1169 {
1170 tokens.add(Token.NOTEQ);
1171 inPos++;
1172 }
1173 else
1174 {
1175 tokens.add(Token.GT);
1176 }
1177 break;
1178 }
1179 case '=':
1180 {
1181 Character nextChar = rule.charAt(++inPos);
1182 if ('<' == nextChar)
1183 {
1184 tokens.add(Token.LEEQ);
1185 inPos++;
1186 }
1187 else if ('>' == nextChar)
1188 {
1189 tokens.add(Token.GTEQ);
1190 inPos++;
1191 }
1192 else
1193 {
1194 tokens.add(Token.EQ);
1195 }
1196 break;
1197 }
1198 case '(':
1199 {
1200 inPos++;
1201 tokens.add(Token.OPEN_PAREN);
1202 state = ParserState.MAY_UMINUS;
1203 break;
1204 }
1205 default:
1206 {
1207 if ('S' == character)
1208 {
1209 tokens.add(Token.START);
1210 inPos++;
1211 }
1212 else if ('E' == character)
1213 {
1214 tokens.add(Token.END);
1215 inPos++;
1216 }
1217 NameAndStream nas = new NameAndStream(rule.substring(inPos), locationDescription);
1218 inPos += nas.getNumberOfChars();
1219 if (nas.isNegated())
1220 {
1221 tokens.add(Token.NEG_VARIABLE);
1222 }
1223 else
1224 {
1225 tokens.add(Token.VARIABLE);
1226 }
1227 Variable variable =
1228 installVariable(nas.getName(), nas.getStream(), EnumSet.noneOf(Flags.class),
1229 locationDescription);
1230 variable.incrementReferenceCount();
1231 tokens.add(variable);
1232 }
1233 }
1234 break;
1235 }
1236 default:
1237 throw new Exception("Error: bad switch; case " + state + " should not happen");
1238 }
1239 }
1240 return tokens.toArray();
1241
1242 }
1243
1244
1245
1246
1247
1248
1249
1250 private boolean stringBeginsWithIgnoreCase(final String sought, final String supplied)
1251 {
1252 if (sought.length() > supplied.length())
1253 {
1254 return false;
1255 }
1256 return (sought.equalsIgnoreCase(supplied.substring(0, sought.length())));
1257 }
1258
1259
1260
1261
1262
1263
1264
1265 private String variableKey(final String name, final short stream)
1266 {
1267 if (name.contains("\t"))
1268 {
1269 System.out.println("Whoops");
1270 }
1271 return String.format("%s%02d", name, stream);
1272 }
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284 private Variable installVariable(String name, short stream, EnumSet<Flags> flags, String location) throws Exception
1285 {
1286 EnumSet<Flags> forbidden = EnumSet.complementOf(EnumSet.of(Flags.HAS_START_RULE, Flags.HAS_END_RULE));
1287 EnumSet<Flags> badFlags = EnumSet.copyOf(forbidden);
1288 badFlags.retainAll(flags);
1289 if (badFlags.size() > 0)
1290 {
1291 throw new Exception("installVariable was called with wrong flag(s): " + badFlags);
1292 }
1293 String key = variableKey(name, stream);
1294 Variable variable = this.variables.get(key);
1295 if (null == variable)
1296 {
1297
1298 variable = new Variable(name, stream);
1299 this.variables.put(key, variable);
1300 if (variable.isDetector())
1301 {
1302 this.detectors.put(key, variable);
1303 }
1304 }
1305 if (flags.contains(Flags.HAS_START_RULE))
1306 {
1307 variable.setStartSource(location);
1308 }
1309 if (flags.contains(Flags.HAS_END_RULE))
1310 {
1311 variable.setEndSource(location);
1312 }
1313 return variable;
1314 }
1315
1316
1317
1318
1319
1320
1321 public static void main(final String[] args) throws Exception
1322 {
1323 OTSModelInterface model = new OTSModelInterface()
1324 {
1325
1326 private static final long serialVersionUID = 20161020L;
1327
1328
1329 private TrafCOD trafCOD;
1330
1331 @Override
1332 public void constructModel(SimulatorInterface<Time, Duration, OTSSimTimeDouble> theSimulator)
1333 throws SimRuntimeException, RemoteException
1334 {
1335 try
1336 {
1337 Network network = new OTSNetwork("TrafCOD test network");
1338 Node nodeX = new OTSNode(network, "Crossing", new OTSPoint3D(0, 0, 0));
1339 Node nodeS = new OTSNode(network, "South", new OTSPoint3D(0, -100, 0));
1340 Node nodeE = new OTSNode(network, "East", new OTSPoint3D(100, 0, 0));
1341 Node nodeN = new OTSNode(network, "North", new OTSPoint3D(0, 100, 0));
1342 Node nodeW = new OTSNode(network, "West", new OTSPoint3D(-100, 0, 0));
1343 Map<GTUType, LongitudinalDirectionality> directionalityMap = new HashMap<>();
1344 Link linkSX = new OTSLink(network, "LinkSX", nodeS, nodeX, LinkType.ALL, null, directionalityMap);
1345 Link linkXN = new OTSLink(network, "LinkSX", nodeX, nodeN, LinkType.ALL, null, directionalityMap);
1346 Link linkWX = new OTSLink(network, "LinkSX", nodeW, nodeX, LinkType.ALL, null, directionalityMap);
1347 Link linkXE = new OTSLink(network, "LinkSX", nodeX, nodeE, LinkType.ALL, null, directionalityMap);
1348 Length laneWidth = new Length(3, LengthUnit.METER);
1349 Speed speedLimit = new Speed(50, SpeedUnit.KM_PER_HOUR);
1350 Lane laneSX =
1351 new Lane((CrossSectionLink) linkSX, "laneSX", Length.ZERO, laneWidth, LaneType.ALL,
1352 LongitudinalDirectionality.DIR_PLUS, speedLimit, new OvertakingConditions.None());
1353 TrafficLightSensor d082 =
1354 new TrafficLightSensor("D082", laneSX, new Length(50, LengthUnit.METER), new Length(20,
1355 LengthUnit.METER), (OTSDEVSSimulatorInterface) theSimulator);
1356 Set<TrafficLight> trafficLights = new HashSet<>();
1357 trafficLights.add(new SimpleTrafficLight("TL08", null, null, (OTSDEVSSimulatorInterface) theSimulator));
1358 trafficLights.add(new SimpleTrafficLight("TL11", null, null, (OTSDEVSSimulatorInterface) theSimulator));
1359 this.trafCOD =
1360 new TrafCOD("Simple TrafCOD controller", "file:///d:/cppb/trafcod/otsim/simpel.tfc", trafficLights,
1361 null, (DEVSSimulator<Time, Duration, OTSSimTimeDouble>) theSimulator);
1362 }
1363 catch (Exception exception)
1364 {
1365 exception.printStackTrace();
1366 }
1367 }
1368
1369 @Override
1370 public SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator() throws RemoteException
1371 {
1372 return this.trafCOD.getSimulator();
1373 }
1374
1375 };
1376 SimpleSimulator testSimulator = new SimpleSimulator(Time.ZERO, Duration.ZERO, new Duration(1, TimeUnit.HOUR), model);
1377 testSimulator.runUpToAndIncluding(new Time(1, TimeUnit.HOUR));
1378
1379 }
1380
1381
1382
1383
1384
1385 protected SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator()
1386 {
1387 return this.simulator;
1388 }
1389
1390
1391
1392
1393
1394 public int getStructureNumber()
1395 {
1396 return this.structureNumber;
1397 }
1398
1399
1400
1401
1402
1403 public void setStructureNumber(int structureNumber)
1404 {
1405 this.structureNumber = structureNumber;
1406 }
1407
1408
1409 @Override
1410 public void updateDetector(String detectorId, boolean detectingGTU)
1411 {
1412 Variable detector = this.detectors.get(detectorId);
1413 detector.setValue(detectingGTU ? 1 : 0, this.currentTime10);
1414 }
1415
1416 }
1417
1418
1419
1420
1421
1422
1423
1424 class NameAndStream
1425 {
1426
1427 private final String name;
1428
1429
1430 private short stream = -1;
1431
1432
1433 private int numberOfChars = 0;
1434
1435
1436 private boolean negated = false;
1437
1438
1439
1440
1441
1442
1443
1444 public NameAndStream(final String text, final String locationDescription) throws Exception
1445 {
1446 int pos = 0;
1447 while (pos < text.length() && Character.isWhitespace(text.charAt(pos)))
1448 {
1449 pos++;
1450 }
1451 while (pos < text.length())
1452 {
1453 char character = text.charAt(pos);
1454 if (!Character.isLetterOrDigit(character))
1455 {
1456 break;
1457 }
1458
1459
1460
1461
1462 pos++;
1463 }
1464 this.numberOfChars = pos;
1465 String trimmed = text.substring(0, pos).replaceAll(" ", "");
1466 if (trimmed.length() == 0)
1467 {
1468 throw new Exception("missing variable at " + locationDescription);
1469 }
1470 if (trimmed.matches("^D([Nn]?\\d\\d\\d)|(\\d\\d\\d[Nn])"))
1471 {
1472
1473 if (trimmed.charAt(1) == 'N' || trimmed.charAt(1) == 'n')
1474 {
1475
1476 trimmed = "D" + trimmed.substring(1, 3) + "N" + trimmed.substring(5);
1477 }
1478 this.name = "D" + trimmed.charAt(3);
1479 this.stream = (short) (10 * (trimmed.charAt(1) - '0') + trimmed.charAt(2) - '0');
1480 return;
1481 }
1482
1483
1484
1485
1486 StringBuilder nameBuilder = new StringBuilder();
1487 for (pos = 0; pos < trimmed.length(); pos++)
1488 {
1489 char nextChar = trimmed.charAt(pos);
1490 if (pos < trimmed.length() - 1 && Character.isDigit(nextChar) && Character.isDigit(trimmed.charAt(pos + 1))
1491 && -1 == this.stream)
1492 {
1493 if (0 == pos || (1 == pos && trimmed.startsWith("N")))
1494 {
1495 throw new Exception("Bad variable name: " + trimmed + " at " + locationDescription);
1496 }
1497 if (trimmed.charAt(pos - 1) == 'N')
1498 {
1499
1500 nameBuilder.deleteCharAt(nameBuilder.length() - 1);
1501
1502 trimmed =
1503 trimmed.substring(0, pos - 2) + trimmed.substring(pos, pos + 2) + "N" + trimmed.substring(pos + 2);
1504 pos -= 2;
1505 }
1506 this.stream = (short) (10 * (trimmed.charAt(pos) - '0') + trimmed.charAt(pos + 1) - '0');
1507 pos++;
1508 }
1509 else
1510 {
1511 nameBuilder.append(nextChar);
1512 }
1513 }
1514 if (trimmed.endsWith("N"))
1515 {
1516 nameBuilder.deleteCharAt(nameBuilder.length() - 1);
1517 this.negated = true;
1518 }
1519 this.name = nameBuilder.toString();
1520 }
1521
1522
1523
1524
1525
1526 public boolean isNegated()
1527 {
1528 return this.negated;
1529 }
1530
1531
1532
1533
1534
1535 public short getStream()
1536 {
1537 return this.stream;
1538 }
1539
1540
1541
1542
1543
1544 public String getName()
1545 {
1546 return this.name;
1547 }
1548
1549
1550
1551
1552
1553 public int getNumberOfChars()
1554 {
1555 return this.numberOfChars;
1556 }
1557
1558
1559 @Override
1560 public String toString()
1561 {
1562 return "NameAndStream [name=" + this.name + ", stream=" + this.stream + ", numberOfChars=" + this.numberOfChars
1563 + ", negated=" + this.negated + "]";
1564 }
1565
1566 }
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577 class Variable
1578 {
1579
1580 EnumSet<Flags> flags = EnumSet.noneOf(Flags.class);
1581
1582
1583 int value;
1584
1585
1586 int timerMax10;
1587
1588
1589 TrafficLightColor color;
1590
1591
1592 final String name;
1593
1594
1595 char listPos;
1596
1597
1598 final short stream;
1599
1600
1601 int refCount;
1602
1603
1604 int updateTime10;
1605
1606
1607 String startSource;
1608
1609
1610 String endSource;
1611
1612
1613 private Set<TrafficLight> trafficLights;
1614
1615
1616
1617
1618
1619
1620 public Variable(final String name, final short stream)
1621 {
1622 this.name = name;
1623 this.stream = stream;
1624 if (name.startsWith("T"))
1625 {
1626 this.flags.add(Flags.IS_TIMER);
1627 }
1628 if (this.name.length() == 2 && this.name.startsWith("D") && Character.isDigit(this.name.charAt(1)))
1629 {
1630 this.flags.add(Flags.IS_DETECTOR);
1631 }
1632 }
1633
1634
1635
1636
1637
1638
1639 public TrafficLightColor getColor() throws Exception
1640 {
1641 if (!this.flags.contains(Flags.IS_OUTPUT))
1642 {
1643 throw new Exception("Stream is not an output");
1644 }
1645 return this.color;
1646 }
1647
1648
1649
1650
1651
1652 public boolean isOutput()
1653 {
1654 return this.flags.contains(Flags.IS_OUTPUT);
1655 }
1656
1657
1658
1659
1660
1661 public boolean isDetector()
1662 {
1663 return this.name.startsWith("D");
1664 }
1665
1666
1667
1668
1669
1670 public void setValue(int newValue, int timeStamp10)
1671 {
1672 if (this.value != newValue)
1673 {
1674 this.updateTime10 = timeStamp10;
1675 setFlag(Flags.CHANGED);
1676 if (0 == newValue)
1677 {
1678 setFlag(Flags.END);
1679 }
1680 else
1681 {
1682 setFlag(Flags.START);
1683 }
1684 if (isOutput())
1685 {
1686 for (TrafficLight trafficLight : this.trafficLights)
1687 {
1688 trafficLight.setTrafficLightColor(this.color);
1689 }
1690 }
1691 }
1692 this.value = newValue;
1693 }
1694
1695
1696
1697
1698
1699
1700 public int getTimerMax() throws Exception
1701 {
1702 if (!this.isTimer())
1703 {
1704 throw new Exception("This is not a timer");
1705 }
1706 return this.timerMax10;
1707 }
1708
1709
1710
1711
1712
1713 public int getValue()
1714 {
1715 return this.value;
1716 }
1717
1718
1719
1720
1721
1722 public void setFlag(final Flags flag)
1723 {
1724 this.flags.add(flag);
1725 }
1726
1727
1728
1729
1730
1731 public void clearFlag(final Flags flag)
1732 {
1733 this.flags.remove(flag);
1734 }
1735
1736
1737
1738
1739
1740 public boolean isTimer()
1741 {
1742 return this.flags.contains(Flags.IS_TIMER);
1743 }
1744
1745
1746
1747
1748 public void clearChangedFlag()
1749 {
1750 this.flags.remove(Flags.CHANGED);
1751 }
1752
1753
1754
1755
1756
1757 public void incrementReferenceCount()
1758 {
1759 this.refCount++;
1760 }
1761
1762
1763
1764
1765
1766 public EnumSet<Flags> getFlags()
1767 {
1768 return EnumSet.copyOf(this.flags);
1769 }
1770
1771
1772
1773
1774
1775 public void addFlag(final Flags flag)
1776 {
1777 this.flags.add(flag);
1778 }
1779
1780
1781
1782
1783
1784
1785
1786 public void setOutput(int colorValue, Set<TrafficLight> trafficLights) throws Exception
1787 {
1788 TrafficLightColor newColor;
1789 switch (colorValue)
1790 {
1791 case 'R':
1792 newColor = TrafficLightColor.RED;
1793 break;
1794 case 'G':
1795 newColor = TrafficLightColor.GREEN;
1796 break;
1797 case 'Y':
1798 newColor = TrafficLightColor.YELLOW;
1799 break;
1800 default:
1801 throw new Exception("Bad color value: " + colorValue);
1802 }
1803
1804 this.color = newColor;
1805 this.flags.add(Flags.IS_OUTPUT);
1806 this.trafficLights = trafficLights;
1807 }
1808
1809
1810
1811
1812
1813
1814 public void setTimerMax(int value10) throws Exception
1815 {
1816 if (!this.flags.contains(Flags.IS_TIMER))
1817 {
1818 throw new Exception("Cannot set maximum timer value of " + selectedFieldsToString(EnumSet.of(PrintFlags.ID)));
1819 }
1820 this.timerMax10 = value10;
1821 }
1822
1823
1824
1825
1826
1827 public String getStartSource()
1828 {
1829 return this.startSource;
1830 }
1831
1832
1833
1834
1835
1836
1837 public void setStartSource(String startSource) throws Exception
1838 {
1839 if (null != this.startSource)
1840 {
1841 throw new Exception("Conflicting rules: " + this.startSource + " vs " + startSource);
1842 }
1843 this.startSource = startSource;
1844 this.flags.add(Flags.HAS_START_RULE);
1845 }
1846
1847
1848
1849
1850
1851 public String getEndSource()
1852 {
1853 return this.endSource;
1854 }
1855
1856
1857
1858
1859
1860
1861 public void setEndSource(String endSource) throws Exception
1862 {
1863 if (null != this.endSource)
1864 {
1865 throw new Exception("Conflicting rules: " + this.startSource + " vs " + endSource);
1866 }
1867 this.endSource = endSource;
1868 this.flags.add(Flags.HAS_END_RULE);
1869 }
1870
1871
1872
1873
1874
1875 public short getStream()
1876 {
1877 return this.stream;
1878 }
1879
1880
1881 @Override
1882 public String toString()
1883 {
1884 return "Variable [" + selectedFieldsToString(EnumSet.of(PrintFlags.ID, PrintFlags.VALUE)) + "]";
1885 }
1886
1887
1888
1889
1890
1891
1892 public String selectedFieldsToString(EnumSet<PrintFlags> printFlags)
1893 {
1894 StringBuilder result = new StringBuilder();
1895 if (printFlags.contains(PrintFlags.ID))
1896 {
1897 if (this.flags.contains(Flags.IS_DETECTOR))
1898 {
1899 result.append("D");
1900 }
1901 else if (printFlags.contains(PrintFlags.INITTIMER))
1902 {
1903 result.append("I");
1904 result.append(this.name);
1905 }
1906 else if (printFlags.contains(PrintFlags.REINITTIMER))
1907 {
1908 result.append("RI");
1909 result.append(this.name);
1910 }
1911 else
1912 {
1913 result.append(this.name);
1914 }
1915 if (this.stream > 0)
1916 {
1917
1918 int pos;
1919 for (pos = 0; pos < result.length(); pos++)
1920 {
1921 if (Character.isDigit(result.charAt(pos)))
1922 {
1923 break;
1924 }
1925 }
1926 result.insert(pos, String.format("%02d", this.stream));
1927 }
1928 if (this.flags.contains(Flags.IS_DETECTOR))
1929 {
1930 result.append(this.name.substring(1));
1931 }
1932 if (printFlags.contains(PrintFlags.NEGATED))
1933 {
1934 result.append("N");
1935 }
1936 }
1937 int printValue = Integer.MIN_VALUE;
1938 if (printFlags.contains(PrintFlags.VALUE))
1939 {
1940 if (printFlags.contains(PrintFlags.NEGATED))
1941 {
1942 printValue = 0 == this.value ? 1 : 0;
1943 }
1944 else
1945 {
1946 printValue = this.value;
1947 }
1948 if (printFlags.contains(PrintFlags.S))
1949 {
1950 if (this.flags.contains(Flags.START))
1951 {
1952 printValue = 1;
1953 }
1954 else
1955 {
1956 printValue = 0;
1957 }
1958 }
1959 if (printFlags.contains(PrintFlags.E))
1960 {
1961 if (this.flags.contains(Flags.END))
1962 {
1963 printValue = 1;
1964 }
1965 else
1966 {
1967 printValue = 0;
1968 }
1969 }
1970 }
1971 if (printFlags.contains(PrintFlags.VALUE) || printFlags.contains(PrintFlags.S) || printFlags.contains(PrintFlags.E)
1972 || printFlags.contains(PrintFlags.FLAGS))
1973 {
1974 result.append("<");
1975 if (printFlags.contains(PrintFlags.VALUE) || printFlags.contains(PrintFlags.S) || printFlags.contains(PrintFlags.E))
1976 {
1977 result.append(printValue);
1978 }
1979 if (printFlags.contains(PrintFlags.FLAGS))
1980 {
1981 if (this.flags.contains(Flags.START))
1982 {
1983 result.append("S");
1984 }
1985 if (this.flags.contains(Flags.END))
1986 {
1987 result.append("E");
1988 }
1989 }
1990 result.append(">");
1991 }
1992 if (printFlags.contains(PrintFlags.MODIFY_TIME))
1993 {
1994 result.append(String.format(" (%d.%d)", this.updateTime10 / 10, this.updateTime10 % 10));
1995 }
1996 return result.toString();
1997 }
1998
1999 }
2000
2001
2002
2003
2004
2005
2006
2007 enum PrintFlags
2008 {
2009
2010 ID,
2011
2012 VALUE,
2013
2014 INITTIMER,
2015
2016 REINITTIMER,
2017
2018 S,
2019
2020 E,
2021
2022 NEGATED,
2023
2024 FLAGS,
2025
2026 MODIFY_TIME,
2027 }
2028
2029
2030
2031
2032
2033
2034
2035 enum Flags
2036 {
2037
2038 START,
2039
2040 END,
2041
2042 TIMEREXPIRED,
2043
2044 CHANGED,
2045
2046 IS_TIMER,
2047
2048 IS_DETECTOR,
2049
2050 HAS_START_RULE,
2051
2052 HAS_END_RULE,
2053
2054 IS_OUTPUT,
2055
2056 INITED,
2057 }