1 package org.opentrafficsim.road.network.lane.object.sensor;
2
3 import java.awt.Color;
4 import java.rmi.RemoteException;
5 import java.util.ArrayList;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import javax.media.j3d.Bounds;
12 import javax.naming.NamingException;
13
14 import org.djunits.value.vdouble.scalar.Length;
15 import org.opentrafficsim.core.compatibility.Compatible;
16 import org.opentrafficsim.core.geometry.OTSGeometryException;
17 import org.opentrafficsim.core.geometry.OTSLine3D;
18 import org.opentrafficsim.core.geometry.OTSPoint3D;
19 import org.opentrafficsim.core.gtu.GTUDirectionality;
20 import org.opentrafficsim.core.gtu.GTUException;
21 import org.opentrafficsim.core.gtu.RelativePosition.TYPE;
22 import org.opentrafficsim.core.network.NetworkException;
23 import org.opentrafficsim.core.network.Node;
24 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
25 import org.opentrafficsim.road.network.animation.SensorAnimation;
26 import org.opentrafficsim.road.network.animation.TrafficLightSensorAnimation;
27 import org.opentrafficsim.road.network.lane.CrossSectionElement;
28 import org.opentrafficsim.road.network.lane.Lane;
29
30 import nl.tudelft.simulation.dsol.animation.Locatable;
31 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
32 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
33 import nl.tudelft.simulation.event.EventInterface;
34 import nl.tudelft.simulation.event.EventListenerInterface;
35 import nl.tudelft.simulation.event.EventProducer;
36 import nl.tudelft.simulation.event.EventProducerInterface;
37 import nl.tudelft.simulation.language.Throw;
38 import nl.tudelft.simulation.language.d3.DirectedPoint;
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class TrafficLightSensor extends EventProducer implements EventListenerInterface, NonDirectionalOccupancySensor,
53 EventProducerInterface, Locatable, Sensor
54 {
55
56 private static final long serialVersionUID = 20161103L;
57
58
59 private final String id;
60
61
62 private final FlankSensor entryA;
63
64
65 private final FlankSensor exitA;
66
67
68 private final FlankSensor entryB;
69
70
71 private final FlankSensor exitB;
72
73
74 private final Set<LaneBasedGTU> currentGTUs = new HashSet<>();
75
76
77 private final Set<Lane> lanes = new HashSet<>();
78
79
80 private final GTUDirectionality directionalityA;
81
82
83 private final GTUDirectionality directionalityB;
84
85
86 private final OTSLine3D path;
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 @SuppressWarnings("checkstyle:parameternumber")
104 public TrafficLightSensor(final String id, final Lane laneA, final Length positionA, final Lane laneB,
105 final Length positionB, final List<Lane> intermediateLanes, final TYPE entryPosition, final TYPE exitPosition,
106 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final Compatible compatible) throws NetworkException
107 {
108 Throw.whenNull(id, "id may not be null");
109 this.id = id;
110 this.entryA = new FlankSensor(id + ".entryA", laneA, positionA, entryPosition, simulator, this, compatible);
111 this.exitA = new FlankSensor(id + ".exitA", laneA, positionA, exitPosition, simulator, this, compatible);
112 this.entryB = new FlankSensor(id + ".entryB", laneB, positionB, entryPosition, simulator, this, compatible);
113 this.exitB = new FlankSensor(id + ".exitB", laneB, positionB, exitPosition, simulator, this, compatible);
114
115
116 this.lanes.add(laneA);
117 if (null != intermediateLanes)
118 {
119 this.lanes.addAll(intermediateLanes);
120 }
121 this.lanes.add(laneB);
122 for (Lane lane : this.lanes)
123 {
124 lane.addListener(this, Lane.GTU_ADD_EVENT);
125 lane.addListener(this, Lane.GTU_REMOVE_EVENT);
126 }
127 if (laneA.equals(laneB))
128 {
129 this.directionalityA = positionA.le(positionB) ? GTUDirectionality.DIR_PLUS : GTUDirectionality.DIR_MINUS;
130 this.directionalityB = this.directionalityA;
131 }
132 else
133 {
134 this.directionalityA = findDirectionality(laneA, intermediateLanes);
135 this.directionalityB =
136 GTUDirectionality.DIR_PLUS == findDirectionality(laneB, intermediateLanes) ? GTUDirectionality.DIR_MINUS
137 : GTUDirectionality.DIR_PLUS;
138
139 }
140 List<OTSPoint3D> outLine = new ArrayList<>();
141 outLine.add(fixElevation(this.entryA.getGeometry().getCentroid()));
142 if (null != intermediateLanes && intermediateLanes.size() > 0)
143 {
144 Lane prevLane = laneA;
145 List<Lane> remainingLanes = new ArrayList<>();
146 remainingLanes.addAll(intermediateLanes);
147 remainingLanes.add(laneB);
148 while (remainingLanes.size() > 0)
149 {
150 Lane continuingLane = null;
151 Node node = prevLane.getParentLink().getEndNode();
152 for (Lane nextLane : intermediateLanes)
153 {
154 if (nextLane.getParentLink().getStartNode().equals(node))
155 {
156 continuingLane = nextLane;
157 outLine.add(fixElevation(nextLane.getCenterLine().getFirst()));
158 break;
159 }
160 else if (nextLane.getParentLink().getEndNode().equals(node))
161 {
162 continuingLane = nextLane;
163 outLine.add(fixElevation(nextLane.getCenterLine().getLast()));
164 break;
165 }
166 }
167 if (null == continuingLane)
168 {
169 throw new NetworkException("Cannot find route from laneA to laneB using the provided intermediateLanes");
170 }
171 remainingLanes.remove(continuingLane);
172 }
173 }
174 outLine.add(fixElevation(this.exitB.getGeometry().getCentroid()));
175 try
176 {
177 this.path = OTSLine3D.createAndCleanOTSLine3D(outLine);
178 }
179 catch (OTSGeometryException exception)
180 {
181
182 throw new NetworkException(exception);
183 }
184 try
185 {
186 new TrafficLightSensorAnimation(this, simulator);
187 }
188 catch (RemoteException | NamingException | OTSGeometryException exception)
189 {
190 throw new NetworkException(exception);
191 }
192 }
193
194
195
196
197
198
199 private OTSPoint3D fixElevation(final OTSPoint3D point)
200 {
201 return new OTSPoint3D(point.x, point.y, point.z + SingleSensor.DEFAULT_SENSOR_ELEVATION.si);
202 }
203
204
205
206
207
208
209
210
211
212 private GTUDirectionality findDirectionality(final Lane lane, final List<Lane> intermediateLanes) throws NetworkException
213 {
214 Node startNode = lane.getParentLink().getStartNode();
215 Node endNode = lane.getParentLink().getEndNode();
216 for (Lane otherLane : intermediateLanes)
217 {
218 if (lane.equals(otherLane))
219 {
220 continue;
221 }
222 Node intermediateNode = otherLane.getParentLink().getStartNode();
223 if (intermediateNode == startNode)
224 {
225 return GTUDirectionality.DIR_MINUS;
226 }
227 if (intermediateNode == endNode)
228 {
229 return GTUDirectionality.DIR_PLUS;
230 }
231 intermediateNode = otherLane.getParentLink().getEndNode();
232 if (intermediateNode == startNode)
233 {
234 return GTUDirectionality.DIR_MINUS;
235 }
236 if (intermediateNode == endNode)
237 {
238 return GTUDirectionality.DIR_PLUS;
239 }
240 }
241 throw new NetworkException("lane " + lane + " is not connected to any intermediate lane or the other lane");
242 }
243
244
245
246
247
248 protected final void addGTU(final LaneBasedGTU gtu)
249 {
250 if (this.currentGTUs.add(gtu) && this.currentGTUs.size() == 1)
251 {
252 fireTimedEvent(NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_ENTRY_EVENT,
253 new Object[] { getId() }, getSimulator().getSimulatorTime());
254 }
255 }
256
257
258
259
260
261 protected final void removeGTU(final LaneBasedGTU gtu)
262 {
263 if (this.currentGTUs.remove(gtu) && this.currentGTUs.size() == 0)
264 {
265 fireTimedEvent(NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_EXIT_EVENT,
266 new Object[] { getId() }, getSimulator().getSimulatorTime());
267 }
268 }
269
270
271 @Override
272 public final void notify(final EventInterface event) throws RemoteException
273 {
274
275 LaneBasedGTU gtu = (LaneBasedGTU) ((Object[]) event.getContent())[1];
276 if (Lane.GTU_REMOVE_EVENT.equals(event.getType()))
277 {
278 if (!this.currentGTUs.contains(gtu))
279 {
280 return;
281 }
282 try
283 {
284 Map<Lane, Length> frontPositions =
285 gtu.positions(gtu.getRelativePositions().get(this.entryA.getPositionType()));
286 Set<Lane> remainingLanes = new HashSet<>(frontPositions.keySet());
287 remainingLanes.retainAll(this.lanes);
288 if (remainingLanes.size() == 0)
289 {
290 removeGTU(gtu);
291 }
292
293
294 return;
295 }
296 catch (GTUException exception)
297 {
298 System.err.println("Caught GTU exception trying to get the frontPositions");
299 exception.printStackTrace();
300 }
301 }
302 else if (Lane.GTU_ADD_EVENT.equals(event.getType()))
303 {
304 if (this.currentGTUs.contains(gtu))
305 {
306 return;
307 }
308
309 try
310 {
311 Map<Lane, Length> frontPositions =
312 gtu.positions(gtu.getRelativePositions().get(this.entryA.getPositionType()));
313 Set<Lane> remainingLanes = new HashSet<>(frontPositions.keySet());
314 remainingLanes.retainAll(this.lanes);
315 if (remainingLanes.size() == 0)
316 {
317 System.err.println("GTU is not in any or our lanes - CANNOT HAPPEN");
318 }
319 Map<Lane, Length> rearPositions =
320 gtu.positions(gtu.getRelativePositions().get(this.exitA.getPositionType()));
321 for (Lane remainingLane : remainingLanes)
322 {
323 Length frontPosition = frontPositions.get(remainingLane);
324 Length rearPosition = rearPositions.get(remainingLane);
325 Length laneLength = remainingLane.getLength();
326
327
328
329 if (frontPosition.lt0() && rearPosition.lt0() || frontPosition.gt(laneLength)
330 && rearPosition.gt(laneLength))
331 {
332 continue;
333 }
334
335 if (this.entryA.getLane() != remainingLane && this.entryB.getLane() != remainingLane)
336 {
337
338 addGTU(gtu);
339 return;
340 }
341
342 GTUDirectionality drivingDirection = gtu.getDirection(remainingLane);
343 if (this.entryA.getLane().equals(this.entryB.getLane()))
344 {
345
346
347 if (GTUDirectionality.DIR_PLUS == drivingDirection)
348 {
349
350 if (this.directionalityA == GTUDirectionality.DIR_PLUS)
351 {
352 if (frontPosition.ge(this.entryA.getLongitudinalPosition())
353 && rearPosition.lt(this.exitB.getLongitudinalPosition()))
354 {
355 addGTU(gtu);
356 return;
357 }
358 }
359 else
360 {
361 if (frontPosition.le(this.entryB.getLongitudinalPosition())
362 && rearPosition.gt(this.exitA.getLongitudinalPosition()))
363 {
364 addGTU(gtu);
365 return;
366 }
367 }
368 }
369 else
370 {
371
372 if (this.directionalityA == GTUDirectionality.DIR_MINUS)
373 {
374 if (frontPosition.le(this.entryB.getLongitudinalPosition())
375 && rearPosition.gt(this.entryA.getLongitudinalPosition()))
376 {
377 addGTU(gtu);
378 return;
379 }
380 }
381 else
382 {
383 if (frontPosition.le(this.entryB.getLongitudinalPosition())
384 && rearPosition.gt(this.exitA.getLongitudinalPosition()))
385 {
386 addGTU(gtu);
387 return;
388 }
389 }
390 }
391 }
392 else
393
394 {
395 Length detectionPosition;
396 GTUDirectionality detectorDirectionality;
397 if (this.entryA.getLane() == remainingLane)
398 {
399 detectionPosition = this.entryA.getLongitudinalPosition();
400 detectorDirectionality = this.directionalityA;
401 }
402 else
403 {
404 detectionPosition = this.entryB.getLongitudinalPosition();
405 detectorDirectionality = this.directionalityB;
406 }
407 if (GTUDirectionality.DIR_PLUS == drivingDirection)
408 {
409 if (GTUDirectionality.DIR_PLUS == detectorDirectionality)
410 {
411 if (frontPosition.ge(detectionPosition))
412 {
413 addGTU(gtu);
414 return;
415 }
416 }
417 else
418 {
419 if (rearPosition.le(detectionPosition))
420 {
421 addGTU(gtu);
422 return;
423 }
424 }
425 }
426 else
427 {
428
429 if (GTUDirectionality.DIR_PLUS == detectorDirectionality)
430 {
431 if (rearPosition.ge(detectionPosition))
432 {
433 addGTU(gtu);
434 return;
435 }
436 }
437 else
438 {
439 if (frontPosition.le(detectionPosition))
440 {
441 addGTU(gtu);
442 return;
443 }
444 }
445 }
446 }
447 }
448 return;
449 }
450 catch (GTUException exception)
451 {
452 System.err.println("Caught GTU exception trying to get the frontPositions");
453 exception.printStackTrace();
454 }
455 }
456 else
457 {
458 System.err.println("Unexpected event: " + event);
459 }
460 }
461
462
463 @Override
464 public final TYPE getPositionTypeEntry()
465 {
466 return this.entryA.getPositionType();
467 }
468
469
470 @Override
471 public final TYPE getPositionTypeExit()
472 {
473 return this.exitA.getPositionType();
474 }
475
476
477 @Override
478 public final Length getLanePositionA()
479 {
480 return this.entryA.getLongitudinalPosition();
481 }
482
483
484 @Override
485 public final Length getLanePositionB()
486 {
487 return this.entryB.getLongitudinalPosition();
488 }
489
490
491
492
493
494
495 final void signalDetection(final FlankSensor sensor, final LaneBasedGTU gtu)
496 {
497 GTUDirectionality gtuDirection = null;
498 try
499 {
500 gtuDirection = gtu.getDirection(sensor.getLane());
501 }
502 catch (GTUException exception)
503 {
504 exception.printStackTrace();
505 }
506
507
508
509
510
511 if (this.entryA == sensor && gtuDirection == this.directionalityA || this.entryB == sensor
512 && gtuDirection != this.directionalityB)
513 {
514 addGTU(gtu);
515 }
516 else if (this.exitA == sensor && gtuDirection != this.directionalityA || this.exitB == sensor
517 && gtuDirection == this.directionalityB)
518
519 {
520 removeGTU(gtu);
521 }
522
523
524
525
526 }
527
528
529 @Override
530 public final String getId()
531 {
532 return this.id;
533 }
534
535
536 @Override
537 public final DEVSSimulatorInterface.TimeDoubleUnit getSimulator()
538 {
539 return this.entryA.getSimulator();
540 }
541
542
543 @Override
544 public final DirectedPoint getLocation() throws RemoteException
545 {
546 return this.path.getLocation();
547 }
548
549
550 @Override
551 public final Bounds getBounds() throws RemoteException
552 {
553 return this.path.getBounds();
554 }
555
556
557
558
559
560 public final OTSLine3D getPath()
561 {
562 return this.path;
563 }
564
565
566
567
568
569 public final boolean getOccupancy()
570 {
571 return this.currentGTUs.size() > 0;
572 }
573
574
575 @Override
576 public final String toString()
577 {
578 return "TrafficLightSensor [id=" + this.id + ", entryA=" + this.entryA + ", exitA=" + this.exitA + ", entryB="
579 + this.entryB + ", exitB=" + this.exitB + ", currentGTUs=" + this.currentGTUs + ", lanes=" + this.lanes
580 + ", directionalityA=" + this.directionalityA + ", directionalityB=" + this.directionalityB + ", path="
581 + this.path + "]";
582 }
583
584 }
585
586
587
588
589 class FlankSensor extends AbstractSensor
590 {
591
592 private static final long serialVersionUID = 20161104L;
593
594
595 private final TrafficLightSensor parent;
596
597
598
599
600
601
602
603
604
605
606
607
608 FlankSensor(final String id, final Lane lane, final Length longitudinalPosition, final TYPE positionType,
609 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final TrafficLightSensor parent, final Compatible compatible)
610 throws NetworkException
611 {
612 super(id, lane, longitudinalPosition, positionType, simulator, compatible);
613 this.parent = parent;
614 try
615 {
616 new SensorAnimation(this, longitudinalPosition, simulator, Color.BLUE);
617 }
618 catch (RemoteException | NamingException exception)
619 {
620 throw new NetworkException(exception);
621 }
622 }
623
624
625 @Override
626 protected void triggerResponse(final LaneBasedGTU gtu)
627 {
628 this.parent.signalDetection(this, gtu);
629 }
630
631
632 @Override
633 public FlankSensor clone(final CrossSectionElement newCSE, final SimulatorInterface.TimeDoubleUnit newSimulator,
634 final boolean animation) throws NetworkException
635 {
636 Throw.when(!(newCSE instanceof Lane), NetworkException.class, "sensors can only be cloned for Lanes");
637 Throw.when(!(newSimulator instanceof DEVSSimulatorInterface.TimeDoubleUnit), NetworkException.class,
638 "simulator should be a DEVSSimulator");
639
640
641 return new FlankSensor(getId(), (Lane) newCSE, getLongitudinalPosition(), getPositionType(),
642 (DEVSSimulatorInterface.TimeDoubleUnit) newSimulator, this.parent, super.getDetectedGTUTypes());
643 }
644
645
646 @Override
647 public final String toString()
648 {
649 return "FlankSensor [parent=" + this.parent.getId() + "]";
650 }
651
652 }