1   /* Copyright 2020 Airbus Defence and Space
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.events.handlers;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.function.Function;
24  import java.util.stream.Collectors;
25  import org.hipparchus.ode.events.Action;
26  import org.orekit.propagation.SpacecraftState;
27  import org.orekit.propagation.events.AbstractDetector;
28  import org.orekit.propagation.events.EventDetector;
29  import org.orekit.time.AbsoluteDate;
30  
31  /**
32   * Facade handlers that allows to use several handlers for one detector.
33   * Otherwise, the use of several detectors, each associated with one handler, that detect
34   * the same event can lead to non-deterministic behaviour.
35   * This handler manages several handlers. The action returned is based on a priority rule
36   * (see {@link #eventOccurred}) :
37   * {@link Action#STOP stop} > {@link Action#RESET_STATE resetState} > {@link Action#RESET_DERIVATIVES resetDerivatives} > {@link Action#RESET_EVENTS resetRevents} > {@link Action#CONTINUE continue}
38   *
39   * @author Lara Hué
40   *
41   * @param <D> object type of the detector which is linked to the handler
42   * @since 10.3
43   */
44  public class EventMultipleHandler<D extends EventDetector> implements EventHandler<D> {
45  
46      /** Default list of handlers for event overrides. */
47      private List<EventHandler<D>> handlers;
48  
49      /** List of handlers whose Action returned is RESET_STATE. */
50      private List<EventHandler<D>> resetStateHandlers;
51  
52      /** Constructor with list initialisation. */
53      public EventMultipleHandler() {
54          handlers = new ArrayList<>();
55          resetStateHandlers = new ArrayList<>();
56      }
57  
58      /** Initialize event handler at the start of a propagation.
59       * <p>
60       * This method is called once at the start of the propagation. It
61       * may be used by the event handler to initialize some internal data
62       * if needed.
63       * </p>
64       * <p>
65       * The default implementation does nothing
66       * </p>
67       * <p>
68       * All handlers' init methods are successively called, the order method is
69       * the order in which handlers are added
70       * </p>
71       * @param initialState initial state
72       * @param target target date for the propagation
73       * @param detector event detector related to the event handler
74       */
75      @Override
76      public void init(final SpacecraftState initialState, final AbsoluteDate target, final D detector) {
77          handlers.forEach(handler -> handler.init(initialState, target, detector));
78      }
79  
80      /**
81       * eventOccurred method mirrors the same interface method as in {@link EventDetector}
82       * and its subclasses, but with an additional parameter that allows the calling
83       * method to pass in an object from the detector which would have potential
84       * additional data to allow the implementing class to determine the correct
85       * return state.
86       *
87       * The MultipleEventHandler class implies a different behaviour on event detections
88       * than with other handlers :
89       * Without the MultipleEventHandler, there is a total order on event occurrences.
90       * Handlers H1, H2, ... that are associated with different instances of
91       * {@link AbstractDetector} are successively called and Action from H1 can prevent
92       * H2 from happening if H1 returned {@link Action#RESET_STATE resetState}.
93       * With the MultipleEventHandler class, when event E occurs, all methods eventOccurred
94       * of Handlers H1, H2... from MultiEventHandler attributes are called, then Action is decided.
95       *
96       * @param s SpaceCraft state to be used in the evaluation
97       * @param detector object with appropriate type that can be used in determining correct return state
98       * @param increasing with the event occurred in an "increasing" or "decreasing" slope direction
99       * @return the Action that the calling detector should pass back to the evaluation system
100      *
101      */
102     @Override
103     public Action eventOccurred(final SpacecraftState s, final D detector, final boolean increasing) {
104         final Map<EventHandler<D>, Action> actions =
105                 handlers.stream().
106                          collect(Collectors.toMap(Function.identity(),
107                              handler -> handler.eventOccurred(s, detector, increasing)));
108 
109         if (actions.containsValue(Action.STOP)) {
110             return Action.STOP;
111         }
112 
113         if (actions.containsValue(Action.RESET_STATE)) {
114             resetStateHandlers = actions.entrySet()
115                                         .stream()
116                                         .filter(entry -> Action.RESET_STATE.equals(entry.getValue()))
117                                         .map(Map.Entry::getKey)
118                                         .collect(Collectors.toList());
119             return Action.RESET_STATE;
120         }
121 
122         if (actions.containsValue(Action.RESET_DERIVATIVES)) {
123             return Action.RESET_DERIVATIVES;
124         }
125 
126         if (actions.containsValue(Action.RESET_EVENTS)) {
127             return Action.RESET_EVENTS;
128         }
129 
130         return Action.CONTINUE;
131     }
132 
133     /** Reset the state prior to continue propagation.
134      * <p>
135      * All handlers that return {@link Action#RESET_STATE resetState} when calling {@link #eventOccurred}
136      * are saved in resetStateHandlers. Their methods resetState are successively called.
137      * The order for calling resetState methods is the order in which handlers are added.
138      * </p>
139      * @param detector object with appropriate type that can be used in determining correct return state
140      * @param oldState old state
141      * @return new state
142      */
143     @Override
144     public SpacecraftState resetState(final D detector, final SpacecraftState oldState) {
145         SpacecraftState newState = oldState;
146         for (EventHandler<D> handler : resetStateHandlers) {
147             newState = handler.resetState(detector, newState);
148         }
149         return newState;
150     }
151 
152     /** Add one handler to the managed handlers list.
153      * @param handler handler associated with D detector
154      * @return this object
155      */
156     public EventMultipleHandler<D> addHandler(final EventHandler<D> handler) {
157         handlers.add(handler);
158         return this;
159     }
160 
161     /** Add several handlers to the managed handlers list.
162      * @param newHandlers handlers associated with D detector
163      * @return this object
164      */
165     @SafeVarargs // this method is safe
166     public final EventMultipleHandler<D> addHandlers(final EventHandler<D>... newHandlers) {
167         Arrays.stream(newHandlers).forEach(this::addHandler);
168         return this;
169     }
170 
171     /** Change handlers list with user input.
172      * @param newHandlers new handlers list associated with D detector
173      *
174      */
175     public void setHandlers(final List<EventHandler<D>> newHandlers) {
176         handlers = newHandlers;
177     }
178 
179     /** Retrieve managed handlers list.
180      * @return list of handlers for event overrides
181      */
182     public List<EventHandler<D>> getHandlers() {
183         return this.handlers;
184     }
185 }