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 }