1   /* Copyright 2020-2025 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   * @since 10.3
42   */
43  public class EventMultipleHandler implements EventHandler {
44  
45      /** Default list of handlers for event overrides. */
46      private List<EventHandler> handlers;
47  
48      /** List of handlers whose Action returned is RESET_STATE. */
49      private List<EventHandler> resetStateHandlers;
50  
51      /** Constructor with list initialisation. */
52      public EventMultipleHandler() {
53          handlers = new ArrayList<>();
54          resetStateHandlers = new ArrayList<>();
55      }
56  
57      /** Initialize event handler at the start of a propagation.
58       * <p>
59       * This method is called once at the start of the propagation. It
60       * may be used by the event handler to initialize some internal data
61       * if needed.
62       * </p>
63       * <p>
64       * The default implementation does nothing
65       * </p>
66       * <p>
67       * All handlers' init methods are successively called, the order method is
68       * the order in which handlers are added
69       * </p>
70       * @param initialState initial state
71       * @param target target date for the propagation
72       * @param detector event detector related to the event handler
73       */
74      @Override
75      public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) {
76          handlers.forEach(handler -> handler.init(initialState, target, detector));
77      }
78  
79      /** Handle an event.
80       *
81       * The MultipleEventHandler class implies a different behaviour on event detections
82       * than with other handlers :
83       * Without the MultipleEventHandler, there is a total order on event occurrences.
84       * Handlers H1, H2, ... that are associated with different instances of
85       * {@link AbstractDetector} are successively called and Action from H1 can prevent
86       * H2 from happening if H1 returned {@link Action#RESET_STATE resetState}.
87       * With the MultipleEventHandler class, when event E occurs, all methods eventOccurred
88       * of Handlers H1, H2... from MultiEventHandler attributes are called, then Action is decided.
89       *
90       * @param s SpaceCraft state to be used in the evaluation
91       * @param detector object with appropriate type that can be used in determining correct return state
92       * @param increasing with the event occurred in an "increasing" or "decreasing" slope direction
93       * @return the Action that the calling detector should pass back to the evaluation system
94       *
95       */
96      @Override
97      public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
98          final Map<EventHandler, Action> actions =
99                  handlers.stream().
100                          collect(Collectors.toMap(Function.identity(),
101                              handler -> handler.eventOccurred(s, detector, increasing)));
102 
103         if (actions.containsValue(Action.STOP)) {
104             return Action.STOP;
105         }
106 
107         if (actions.containsValue(Action.RESET_STATE)) {
108             resetStateHandlers = actions.entrySet()
109                                         .stream()
110                                         .filter(entry -> Action.RESET_STATE.equals(entry.getValue()))
111                                         .map(Map.Entry::getKey)
112                                         .collect(Collectors.toList());
113             return Action.RESET_STATE;
114         }
115 
116         if (actions.containsValue(Action.RESET_DERIVATIVES)) {
117             return Action.RESET_DERIVATIVES;
118         }
119 
120         if (actions.containsValue(Action.RESET_EVENTS)) {
121             return Action.RESET_EVENTS;
122         }
123 
124         return Action.CONTINUE;
125     }
126 
127     /** Reset the state prior to continue propagation.
128      * <p>
129      * All handlers that return {@link Action#RESET_STATE resetState} when calling {@link #eventOccurred}
130      * are saved in resetStateHandlers. Their methods resetState are successively called.
131      * The order for calling resetState methods is the order in which handlers are added.
132      * </p>
133      * @param detector object with appropriate type that can be used in determining correct return state
134      * @param oldState old state
135      * @return new state
136      */
137     @Override
138     public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
139         SpacecraftState newState = oldState;
140         for (EventHandler handler : resetStateHandlers) {
141             newState = handler.resetState(detector, newState);
142         }
143         return newState;
144     }
145 
146     @Override
147     public void finish(final SpacecraftState finalState, final EventDetector detector) {
148         for (final EventHandler handler : handlers) {
149             handler.finish(finalState, detector);
150         }
151     }
152 
153     /** Add one handler to the managed handlers list.
154      * @param handler handler associated with D detector
155      * @return this object
156      */
157     public EventMultipleHandler addHandler(final EventHandler handler) {
158         handlers.add(handler);
159         return this;
160     }
161 
162     /** Add several handlers to the managed handlers list.
163      * @param newHandlers handlers associated with D detector
164      * @return this object
165      */
166     @SafeVarargs // this method is safe
167     public final EventMultipleHandler addHandlers(final EventHandler... newHandlers) {
168         Arrays.stream(newHandlers).forEach(this::addHandler);
169         return this;
170     }
171 
172     /** Change handlers list with user input.
173      * @param newHandlers new handlers list associated with D detector
174      *
175      */
176     public void setHandlers(final List<EventHandler> newHandlers) {
177         handlers = newHandlers;
178     }
179 
180     /** Retrieve managed handlers list.
181      * @return list of handlers for event overrides
182      */
183     public List<EventHandler> getHandlers() {
184         return this.handlers;
185     }
186 }