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 }