1 package org.opentrafficsim.road.gtu.lane.tactical.util.lmrs;
2
3 import java.util.SortedSet;
4
5 import org.djunits.value.vdouble.scalar.Acceleration;
6 import org.djunits.value.vdouble.scalar.Duration;
7 import org.djunits.value.vdouble.scalar.Length;
8 import org.djunits.value.vdouble.scalar.Speed;
9 import org.djutils.exceptions.Try;
10 import org.opentrafficsim.base.parameters.ParameterException;
11 import org.opentrafficsim.base.parameters.ParameterTypes;
12 import org.opentrafficsim.base.parameters.Parameters;
13 import org.opentrafficsim.core.gtu.GtuException;
14 import org.opentrafficsim.core.gtu.perception.EgoPerception;
15 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
16 import org.opentrafficsim.core.network.LateralDirectionality;
17 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
18 import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
19 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
20 import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
21 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
22 import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
23 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
24 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
25 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
26 import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
27 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
28 import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
29 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
30
31
32
33
34
35
36
37
38
39
40
41 public interface Synchronization extends LmrsParameters
42 {
43
44
45 Synchronization PASSIVE = new Synchronization()
46 {
47
48 @Override
49 public Acceleration synchronize(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
50 final CarFollowingModel cfm, final double desire, final LateralDirectionality lat, final LmrsData lmrsData,
51 final LaneChange laneChange, final LateralDirectionality initiatedLaneChange)
52 throws ParameterException, OperationalPlanException
53 {
54 Acceleration a = Acceleration.POSITIVE_INFINITY;
55 double dCoop = params.getParameter(DCOOP);
56 RelativeLane relativeLane = new RelativeLane(lat, 1);
57
58 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> set =
59 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(relativeLane);
60 HeadwayGtu leader = null;
61 if (set != null)
62 {
63 if (desire >= dCoop && !set.isEmpty())
64 {
65 leader = set.first();
66 }
67 else
68 {
69 for (HeadwayGtu gtu : set)
70 {
71 if (gtu.getSpeed().gt0())
72 {
73 leader = gtu;
74 break;
75 }
76 }
77 }
78 }
79 Speed ownSpeed = perception.getPerceptionCategory(EgoPerception.class).getSpeed();
80 if (leader != null)
81 {
82 Length headway = headwayWithLcSpace(leader, params, laneChange);
83 Acceleration aSingle =
84 LmrsUtil.singleAcceleration(headway, ownSpeed, leader.getSpeed(), desire, params, sli, cfm);
85 a = Acceleration.min(a, aSingle);
86 a = gentleUrgency(a, desire, params);
87 }
88
89 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> leaders =
90 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.CURRENT);
91 if (!leaders.isEmpty() && leaders.first().getSpeed().lt(params.getParameter(ParameterTypes.VCONG)))
92 {
93 Length headway = leaders.first().getDistance().minus(laneChange.getMinimumLaneChangeDistance());
94 Acceleration aSingle =
95 LmrsUtil.singleAcceleration(headway, ownSpeed, leaders.first().getSpeed(), desire, params, sli, cfm);
96 aSingle = gentleUrgency(aSingle, desire, params);
97 a = Acceleration.min(a, aSingle);
98 }
99
100
101 Length xMerge =
102 getMergeDistance(perception, lat).minus(perception.getPerceptionCategory(EgoPerception.class).getLength());
103 if (xMerge.gt0())
104 {
105 Acceleration aMerge = LmrsUtil.singleAcceleration(xMerge, ownSpeed, Speed.ZERO, desire, params, sli, cfm);
106 a = Acceleration.max(a, aMerge);
107 }
108 return a;
109 }
110
111
112 @Override
113 public String toString()
114 {
115 return "PASSIVE";
116 }
117 };
118
119
120
121
122
123
124
125 Synchronization ALIGN_GAP = new Synchronization()
126 {
127
128 @Override
129 public Acceleration synchronize(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
130 final CarFollowingModel cfm, final double desire, final LateralDirectionality lat, final LmrsData lmrsData,
131 final LaneChange laneChange, final LateralDirectionality initiatedLaneChange)
132 throws ParameterException, OperationalPlanException
133 {
134 Acceleration a = Acceleration.POSITIVE_INFINITY;
135 EgoPerception<?, ?> ego = perception.getPerceptionCategory(EgoPerception.class);
136 Speed ownSpeed = ego.getSpeed();
137 RelativeLane relativeLane = new RelativeLane(lat, 1);
138 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> leaders =
139 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(relativeLane);
140 if (!leaders.isEmpty())
141 {
142 HeadwayGtu leader = leaders.first();
143 Length gap = leader.getDistance();
144 LmrsUtil.setDesiredHeadway(params, desire);
145 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> followers =
146 perception.getPerceptionCategory(NeighborsPerception.class).getFollowers(relativeLane);
147 if (!followers.isEmpty())
148 {
149 HeadwayGtu follower = followers.first();
150 Length netGap = leader.getDistance().plus(follower.getDistance()).times(0.5);
151 gap = Length.max(gap, leader.getDistance().minus(netGap).plus(cfm.desiredHeadway(params, ownSpeed)));
152 }
153 a = CarFollowingUtil.followSingleLeader(cfm, params, ownSpeed, sli, gap, leader.getSpeed());
154 LmrsUtil.resetDesiredHeadway(params);
155
156 a = gentleUrgency(a, desire, params);
157 }
158
159 Length xMerge = getMergeDistance(perception, lat);
160 if (xMerge.gt0())
161 {
162 Acceleration aMerge = LmrsUtil.singleAcceleration(xMerge, ownSpeed, Speed.ZERO, desire, params, sli, cfm);
163 a = Acceleration.max(a, aMerge);
164 }
165 return a;
166 }
167
168
169 @Override
170 public String toString()
171 {
172 return "ALIGN_GAP";
173 }
174 };
175
176
177 Synchronization PASSIVE_MOVING = new Synchronization()
178 {
179
180 @Override
181 public Acceleration synchronize(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
182 final CarFollowingModel cfm, final double desire, final LateralDirectionality lat, final LmrsData lmrsData,
183 final LaneChange laneChange, final LateralDirectionality initiatedLaneChange)
184 throws ParameterException, OperationalPlanException
185 {
186 double dCoop = params.getParameter(DCOOP);
187 Speed ownSpeed = perception.getPerceptionCategory(EgoPerception.class).getSpeed();
188 if (desire < dCoop && ownSpeed.si < params.getParameter(ParameterTypes.LOOKAHEAD).si
189 / params.getParameter(ParameterTypes.T0).si)
190 {
191 return Acceleration.POSITIVE_INFINITY;
192 }
193 return PASSIVE.synchronize(perception, params, sli, cfm, desire, lat, lmrsData, laneChange, initiatedLaneChange);
194 }
195
196
197 @Override
198 public String toString()
199 {
200 return "PASSIVE_MOVING";
201 }
202 };
203
204
205 Synchronization ACTIVE = new Synchronization()
206 {
207
208 @Override
209 public Acceleration synchronize(final LanePerception perception, final Parameters params, final SpeedLimitInfo sli,
210 final CarFollowingModel cfm, final double desire, final LateralDirectionality lat, final LmrsData lmrsData,
211 final LaneChange laneChange, final LateralDirectionality initiatedLaneChange)
212 throws ParameterException, OperationalPlanException
213 {
214
215 Acceleration b = params.getParameter(ParameterTypes.B);
216 Duration tMin = params.getParameter(ParameterTypes.TMIN);
217 Duration tMax = params.getParameter(ParameterTypes.TMAX);
218 Speed vCong = params.getParameter(ParameterTypes.VCONG);
219 Length x0 = params.getParameter(ParameterTypes.LOOKAHEAD);
220 Duration t0 = params.getParameter(ParameterTypes.T0);
221 Duration lc = params.getParameter(ParameterTypes.LCDUR);
222 Speed tagSpeed = x0.divide(t0);
223 double dCoop = params.getParameter(DCOOP);
224 EgoPerception<?, ?> ego = perception.getPerceptionCategory(EgoPerception.class);
225 Speed ownSpeed = ego.getSpeed();
226 Length ownLength = ego.getLength();
227 Length dx;
228 try
229 {
230 dx = perception.getGtu().getFront().dx();
231 }
232 catch (GtuException exception)
233 {
234 throw new OperationalPlanException(exception);
235 }
236
237
238 InfrastructurePerception infra = perception.getPerceptionCategory(InfrastructurePerception.class);
239 SortedSet<InfrastructureLaneChangeInfo> info = infra.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT);
240
241
242 Length xMerge = getMergeDistance(perception, lat);
243 int nCur = 0;
244 Length xCur = Length.POSITIVE_INFINITY;
245 for (InfrastructureLaneChangeInfo lcInfo : info)
246 {
247 int nCurTmp = lcInfo.getRequiredNumberOfLaneChanges();
248
249 Length xCurTmp = lcInfo.getRemainingDistance().minus(ownLength.times(2.0 * nCurTmp)).minus(dx);
250 if (xCurTmp.lt(xCur))
251 {
252 nCur = nCurTmp;
253 xCur = xCurTmp;
254 }
255 }
256
257
258
259 Length xMergeSync = xCur.minus(Length.instantiateSI(.5 * ownSpeed.si * ownSpeed.si / b.si));
260 xMergeSync = Length.min(xMerge, xMergeSync);
261
262
263 NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
264 RelativeLane lane = new RelativeLane(lat, 1);
265 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> leaders = neighbors.getLeaders(lane);
266 HeadwayGtu syncVehicle = lmrsData.getSyncVehicle(leaders);
267 if (syncVehicle != null && ((syncVehicle.getSpeed().lt(vCong) && syncVehicle.getDistance().lt(xMergeSync))
268 || syncVehicle.getDistance().gt(xCur)))
269 {
270 syncVehicle = null;
271 }
272
273
274 if (leaders != null && syncVehicle == null)
275 {
276 Length maxDistance = Length.min(x0, xCur);
277 for (HeadwayGtu leader : leaders)
278 {
279 if (leader.getDistance().lt(maxDistance))
280 {
281 if ((leader.getDistance().gt(xMergeSync) || leader.getSpeed().gt(vCong))
282 && tagAlongAcceleration(leader, ownSpeed, ownLength, tagSpeed, desire, params, sli, cfm)
283 .gt(b.neg()))
284 {
285 syncVehicle = leader;
286 break;
287 }
288 }
289 else
290 {
291 break;
292 }
293 }
294 }
295
296
297 HeadwayGtu up;
298 PerceptionCollectable<HeadwayGtu, LaneBasedGtu> followers = neighbors.getFollowers(lane);
299 HeadwayGtu follower = followers == null || followers.isEmpty() ? null
300 : followers.first().moved(
301 followers.first().getDistance().plus(ownLength).plus(followers.first().getLength()).neg(),
302 followers.first().getSpeed(), followers.first().getAcceleration());
303 boolean upOk;
304 if (syncVehicle == null)
305 {
306 up = null;
307 upOk = false;
308 }
309 else
310 {
311 up = getFollower(syncVehicle, leaders, follower, ownLength);
312 upOk = up == null ? false
313 : tagAlongAcceleration(up, ownSpeed, ownLength, tagSpeed, desire, params, sli, cfm).gt(b.neg());
314 }
315 while (syncVehicle != null
316 && up != null && (upOk || (!canBeAhead(up, xCur, nCur, ownSpeed, ownLength, tagSpeed, dCoop, b, tMin, tMax,
317 x0, t0, lc, desire) && desire > dCoop))
318 && (up.getDistance().gt(xMergeSync) || up.getSpeed().gt(vCong)))
319 {
320 if (up.equals(follower))
321 {
322
323 syncVehicle = null;
324 up = null;
325 break;
326 }
327 syncVehicle = up;
328 up = getFollower(syncVehicle, leaders, follower, ownLength);
329 upOk = up == null ? false
330 : tagAlongAcceleration(up, ownSpeed, ownLength, tagSpeed, desire, params, sli, cfm).gt(b.neg());
331 }
332 lmrsData.setSyncVehicle(syncVehicle);
333
334
335 Acceleration a = Acceleration.POSITIVE_INFINITY;
336 if (syncVehicle != null)
337 {
338 a = gentleUrgency(tagAlongAcceleration(syncVehicle, ownSpeed, ownLength, tagSpeed, desire, params, sli, cfm),
339 desire, params);
340 }
341 else if (nCur > 0 && (follower != null || (leaders != null && !leaders.isEmpty())))
342 {
343
344 if (follower != null && !canBeAhead(follower, xCur, nCur, ownSpeed, ownLength, tagSpeed, dCoop, b, tMin, tMax,
345 x0, t0, lc, desire))
346 {
347
348 double c = requiredBufferSpace(ownSpeed, nCur, x0, t0, lc, dCoop).si;
349 double t = (xCur.si - follower.getDistance().si - c) / follower.getSpeed().si;
350 double xGap = ownSpeed.si * (tMin.si + desire * (tMax.si - tMin.si));
351 Acceleration acc = Acceleration.instantiateSI(2 * (xCur.si - c - ownSpeed.si * t - xGap) / (t * t));
352 if (follower.getSpeed().eq0() || acc.si < -ownSpeed.si / t || t < 0)
353 {
354
355
356 a = stopForEnd(xCur, xMerge, params, ownSpeed, cfm, sli);
357 }
358 else
359 {
360 a = gentleUrgency(acc, desire, params);
361 }
362 }
363 else if (!LmrsUtil.acceptLaneChange(perception, params, sli, cfm, desire, ownSpeed, Acceleration.ZERO, lat,
364 lmrsData.getGapAcceptance(), laneChange))
365 {
366 a = stopForEnd(xCur, xMerge, params, ownSpeed, cfm, sli);
367
368 if (leaders != null && !leaders.isEmpty())
369 {
370 double c = requiredBufferSpace(ownSpeed, nCur, x0, t0, lc, dCoop).si;
371 double t = (xCur.si - leaders.first().getDistance().si - c) / leaders.first().getSpeed().si;
372 double xGap = ownSpeed.si * (tMin.si + desire * (tMax.si - tMin.si));
373 Acceleration acc = Acceleration.instantiateSI(2 * (xCur.si - c - ownSpeed.si * t - xGap) / (t * t));
374 if (!(leaders.first().getSpeed().eq0() || acc.si < -ownSpeed.si / t || t < 0))
375 {
376 a = Acceleration.max(a, acc);
377 }
378 }
379 }
380 }
381
382
383 if (nCur > 1)
384 {
385 if (xMerge.gt0())
386 {
387
388 Speed vMerge = xCur.lt(xMerge) ? Speed.ZERO
389 : xCur.minus(xMerge).divide(t0.times((1 - dCoop) * (nCur - 1)).plus(lc));
390 vMerge = Speed.max(vMerge, x0.divide(t0));
391 a = Acceleration.min(a, CarFollowingUtil.approachTargetSpeed(cfm, params, ownSpeed, sli, xMerge, vMerge));
392 }
393 else
394 {
395
396 Length c = requiredBufferSpace(ownSpeed, nCur, x0, t0, lc, dCoop);
397 if (xCur.lt(c))
398 {
399 a = Acceleration.min(a, b.neg());
400 }
401 }
402 }
403 return a;
404 }
405
406
407 @Override
408 public String toString()
409 {
410 return "ACTIVE";
411 }
412 };
413
414
415
416
417
418
419
420
421 public static Length getMergeDistance(final LanePerception perception, final LateralDirectionality lat)
422 throws OperationalPlanException
423 {
424 InfrastructurePerception infra = perception.getPerceptionCategory(InfrastructurePerception.class);
425 Length dx = Try.assign(() -> perception.getGtu().getFront().dx(), "Could not obtain GTU.");
426 Length xMergeRef = infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, lat);
427 if (xMergeRef.gt0() && xMergeRef.lt(dx))
428 {
429 return Length.ZERO;
430 }
431 Length xMerge = xMergeRef.minus(dx);
432 return xMerge.lt0() ? xMerge.neg() : Length.ZERO;
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 Acceleration synchronize(LanePerception perception, Parameters params, SpeedLimitInfo sli, CarFollowingModel cfm,
451 double desire, LateralDirectionality lat, LmrsData lmrsData, LaneChange laneChange,
452 LateralDirectionality initiatedLaneChange) throws ParameterException, OperationalPlanException;
453
454
455
456
457
458
459
460
461
462 static Length headwayWithLcSpace(final Headway headway, final Parameters parameters, final LaneChange laneChange)
463 throws ParameterException
464 {
465 if (headway.getSpeed().gt(parameters.getParameter(ParameterTypes.VCONG)))
466 {
467 return headway.getDistance();
468 }
469 return headway.getDistance().minus(laneChange.getMinimumLaneChangeDistance());
470 }
471
472
473
474
475
476
477
478
479
480
481 static Acceleration gentleUrgency(final Acceleration a, final double desire, final Parameters params)
482 throws ParameterException
483 {
484 Acceleration b = params.getParameter(ParameterTypes.B);
485 if (a.si > -b.si)
486 {
487 return a;
488 }
489 double dCoop = params.getParameter(DCOOP);
490 if (desire < dCoop)
491 {
492 return b.neg();
493 }
494 Acceleration bCrit = params.getParameter(ParameterTypes.BCRIT);
495 double f = (desire - dCoop) / (1.0 - dCoop);
496 Acceleration lim = Acceleration.interpolate(b.neg(), bCrit.neg(), f);
497 return Acceleration.max(a, lim);
498 }
499
500
501
502
503
504
505
506
507
508 static HeadwayGtu getFollower(final HeadwayGtu gtu, final PerceptionCollectable<HeadwayGtu, LaneBasedGtu> leaders,
509 final HeadwayGtu follower, final Length ownLength)
510 {
511 HeadwayGtu last = null;
512 for (HeadwayGtu leader : leaders)
513 {
514 if (leader.equals(gtu))
515 {
516 return last == null ? follower : last;
517 }
518 last = leader;
519 }
520 return null;
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536 @SuppressWarnings("checkstyle:parameternumber")
537 static Acceleration tagAlongAcceleration(final HeadwayGtu leader, final Speed followerSpeed, final Length followerLength,
538 final Speed tagSpeed, final double desire, final Parameters params, final SpeedLimitInfo sli,
539 final CarFollowingModel cfm) throws ParameterException
540 {
541 double dCoop = params.getParameter(DCOOP);
542 double tagV = followerSpeed.lt(tagSpeed) ? 1.0 - followerSpeed.si / tagSpeed.si : 0.0;
543 double tagD = desire <= dCoop ? 1.0 : 1.0 - (desire - dCoop) / (1.0 - dCoop);
544 double tagExtent = tagV < tagD ? tagV : tagD;
545
546
547
548
549
550
551
552
553
554
555
556 Length headwayAdjustment = params.getParameter(ParameterTypes.S0)
557 .plus(Length.min(followerLength, leader.getLength()).times(0.5)).times(tagExtent);
558 Acceleration a = LmrsUtil.singleAcceleration(leader.getDistance().plus(headwayAdjustment), followerSpeed,
559 leader.getSpeed(), desire, params, sli, cfm);
560 return a;
561 }
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582 static boolean canBeAhead(final HeadwayGtu adjacentVehicle, final Length xCur, final int nCur, final Speed ownSpeed,
583 final Length ownLength, final Speed tagSpeed, final double dCoop, final Acceleration b, final Duration tMin,
584 final Duration tMax, final Length x0, final Duration t0, final Duration lc, final double desire)
585 throws ParameterException
586 {
587
588
589 boolean tmp = LmrsUtil
590 .singleAcceleration(adjacentVehicle.getDistance().neg().minus(adjacentVehicle.getLength()).minus(ownLength),
591 adjacentVehicle.getSpeed(), ownSpeed, desire, adjacentVehicle.getParameters(),
592 adjacentVehicle.getSpeedLimitInfo(), adjacentVehicle.getCarFollowingModel())
593 .gt(b.neg());
594 if (adjacentVehicle.getDistance().lt(ownLength.neg())
595 && ((desire > dCoop && tmp) || (ownSpeed.lt(tagSpeed) && adjacentVehicle.getSpeed().lt(tagSpeed))))
596 {
597 return true;
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612 Length c = requiredBufferSpace(ownSpeed, nCur, x0, t0, lc, dCoop);
613 double t = (xCur.si - c.si) / ownSpeed.si;
614 double xGap = adjacentVehicle.getSpeed().si * (tMin.si + desire * (tMax.si - tMin.si));
615 return 0.0 < t && t < (xCur.si - adjacentVehicle.getDistance().si - ownLength.si - adjacentVehicle.getLength().si - c.si
616 - xGap) / adjacentVehicle.getSpeed().si;
617 }
618
619
620
621
622
623
624
625
626
627
628
629 static Length requiredBufferSpace(final Speed speed, final int nCur, final Length x0, final Duration t0, final Duration lc,
630 final double dCoop)
631 {
632 Length xCrit = speed.times(t0);
633 xCrit = Length.max(xCrit, x0);
634 return speed.times(lc).plus(xCrit.times((nCur - 1.0) * (1.0 - dCoop)));
635 }
636
637
638
639
640
641
642
643
644
645
646
647
648 static Acceleration stopForEnd(final Length xCur, final Length xMerge, final Parameters params, final Speed ownSpeed,
649 final CarFollowingModel cfm, final SpeedLimitInfo sli) throws ParameterException
650 {
651 if (xCur.lt0())
652 {
653
654 return Acceleration.max(params.getParameter(ParameterTypes.BCRIT).neg(),
655 CarFollowingUtil.stop(cfm, params, ownSpeed, sli, xMerge));
656 }
657 LmrsUtil.setDesiredHeadway(params, 1.0);
658 Acceleration a = CarFollowingUtil.stop(cfm, params, ownSpeed, sli, xCur);
659 if (a.lt0())
660 {
661
662 a = Acceleration.min(a, params.getParameter(ParameterTypes.B).neg());
663
664 if (xMerge.gt0())
665 {
666 a = Acceleration.max(a, CarFollowingUtil.stop(cfm, params, ownSpeed, sli, xMerge));
667 }
668 }
669 else
670 {
671 a = Acceleration.POSITIVE_INFINITY;
672 }
673 LmrsUtil.resetDesiredHeadway(params);
674 return a;
675 }
676
677
678
679
680
681
682
683 static HeadwayGtu getTargetLeader(final HeadwayGtu gtu, final SortedSet<HeadwayGtu> leaders)
684 {
685 for (HeadwayGtu leader : leaders)
686 {
687 if (leader.getDistance().gt(gtu.getDistance()))
688 {
689 return leader;
690 }
691 }
692 return null;
693 }
694
695 }