1   /* Copyright 2002-2025 CS GROUP
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;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.hipparchus.ode.events.Action;
23  import org.orekit.propagation.SpacecraftState;
24  import org.orekit.propagation.events.handlers.EventHandler;
25  import org.orekit.time.AbsoluteDate;
26  import org.orekit.time.TimeStamped;
27  
28  /** This class logs events detectors events during propagation.
29   *
30   * <p>As {@link EventDetector events detectors} are triggered during
31   * orbit propagation, an event specific {@link
32   * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred}
33   * method is called. This class can be used to add a global logging
34   * feature registering all events with their corresponding states in
35   * a chronological sequence (or reverse-chronological if propagation
36   * occurs backward).
37   * <p>This class works by wrapping user-provided {@link EventDetector
38   * events detectors} before they are registered to the propagator. The
39   * wrapper monitor the calls to {@link
40   * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred}
41   * and store the corresponding events as {@link LoggedEvent} instances.
42   * After propagation is complete, the user can retrieve all the events
43   * that have occurred at once by calling method {@link #getLoggedEvents()}.</p>
44   *
45   * @author Luc Maisonobe
46   */
47  public class EventsLogger {
48  
49      /** List of occurred events. */
50      private final List<LoggedEvent> log;
51  
52      /** Simple constructor.
53       * <p>
54       * Build an empty logger for events detectors.
55       * </p>
56       */
57      public EventsLogger() {
58          log = new ArrayList<>();
59      }
60  
61      /** Monitor an event detector.
62       * <p>
63       * In order to monitor an event detector, it must be wrapped thanks to
64       * this method as follows:
65       * </p>
66       * <pre>
67       * Propagator propagator = new XyzPropagator(...);
68       * EventsLogger logger = new EventsLogger();
69       * EventDetector detector = new UvwDetector(...);
70       * propagator.addEventDetector(logger.monitorDetector(detector));
71       * </pre>
72       * <p>
73       * Note that the event detector returned by the {@link
74       * LoggedEvent#getEventDetector() getEventDetector} method in
75       * {@link LoggedEvent LoggedEvent} instances returned by {@link
76       * #getLoggedEvents()} are the {@code monitoredDetector} instances
77       * themselves, not the wrapping detector returned by this method.
78       * </p>
79       * @param monitoredDetector event detector to monitor
80       * @return the wrapping detector to add to the propagator
81       * @param <T> class type for the generic version
82       */
83      public <T extends EventDetector> EventDetector monitorDetector(final T monitoredDetector) {
84          return new LoggingWrapper(monitoredDetector);
85      }
86  
87      /** Clear the logged events.
88       */
89      public void clearLoggedEvents() {
90          log.clear();
91      }
92  
93      /** Get an immutable copy of the logged events.
94       * <p>
95       * The copy is independent of the logger. It is preserved
96       * event if the {@link #clearLoggedEvents() clearLoggedEvents} method
97       * is called and the logger reused in another propagation.
98       * </p>
99       * @return an immutable copy of the logged events
100      */
101     public List<LoggedEvent> getLoggedEvents() {
102         return new ArrayList<>(log);
103     }
104 
105     /** Class for logged events entries. */
106     public static class LoggedEvent implements TimeStamped {
107 
108         /** Event detector triggered. */
109         private final EventDetector detector;
110 
111         /** Triggering state. */
112         private final SpacecraftState state;
113 
114         /** Increasing/decreasing status. */
115         private final boolean increasing;
116 
117         /** State after reset if any, otherwise same than triggering state. */
118         private final SpacecraftState resetState;
119 
120         /** Constructor.
121          * @param detector detector for event that was triggered
122          * @param state state at event trigger date
123          * @param resetState state after reset if any, otherwise same as event state
124          * @param increasing indicator if the event switching function was increasing
125          * or decreasing at event occurrence date
126          * @since 13.1
127          */
128         private LoggedEvent(final EventDetector detector, final SpacecraftState state,
129                             final SpacecraftState resetState, final boolean increasing) {
130             this.detector   = detector;
131             this.state      = state;
132             this.resetState = resetState;
133             this.increasing = increasing;
134         }
135 
136         /** Get the event detector triggered.
137          * @return event detector triggered
138          */
139         public EventDetector getEventDetector() {
140             return detector;
141         }
142 
143         /** {@inheritDoc} */
144         @Override
145         public AbsoluteDate getDate() {
146             return state.getDate();
147         }
148 
149         /** Get the triggering state.
150          * @return triggering state
151          * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean)
152          */
153         public SpacecraftState getState() {
154             return state;
155         }
156 
157         /** Get the reset state.
158          * @return reset state
159          * @see EventHandler#resetState(EventDetector, SpacecraftState)
160          * @since 13.1
161          */
162         public SpacecraftState getResetState() {
163             return resetState;
164         }
165 
166         /** Get the Increasing/decreasing status of the event.
167          * @return increasing/decreasing status of the event
168          * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean)
169          */
170         public boolean isIncreasing() {
171             return increasing;
172         }
173 
174     }
175 
176     /** Internal wrapper for events detectors. */
177     private class LoggingWrapper implements DetectorModifier {
178 
179         /** Wrapped event detector. */
180         private final EventDetector wrappedDetector;
181 
182         /** Simple constructor.
183          * @param detector events detector to wrap
184          */
185         LoggingWrapper(final EventDetector detector) {
186             this.wrappedDetector = detector;
187         }
188 
189         /** Log an event.
190          * @param state state at event trigger date
191          * @param resetState state after reset if any, otherwise event state
192          * @param increasing indicator if the event switching function was increasing
193          */
194         void logEvent(final SpacecraftState state, final SpacecraftState resetState, final boolean increasing) {
195             log.add(new LoggedEvent(getDetector(), state, resetState, increasing));
196         }
197 
198         /** {@inheritDoc} */
199         @Override
200         public EventDetector getDetector() {
201             return wrappedDetector;
202         }
203 
204         /** {@inheritDoc} */
205         @Override
206         public EventHandler getHandler() {
207             final EventHandler handler = getDetector().getHandler();
208 
209             return new EventHandler() {
210 
211                 private SpacecraftState lastTriggeringState = null;
212                 private SpacecraftState lastResetState = null;
213 
214                 /** {@inheritDoc} */
215                 @Override
216                 public void init(final SpacecraftState initialState, final AbsoluteDate target,
217                                  final EventDetector detector) {
218                     EventHandler.super.init(initialState, target, detector);
219                     lastTriggeringState = null;
220                     lastResetState = null;
221                 }
222 
223                 /** {@inheritDoc} */
224                 @Override
225                 public Action eventOccurred(final SpacecraftState s, final EventDetector d, final boolean increasing) {
226                     final Action action = handler.eventOccurred(s, getDetector(), increasing);
227                     if (action == Action.RESET_STATE) {
228                         lastResetState = resetState(getDetector(), s);
229                     } else {
230                         lastResetState = s;
231                     }
232                     lastTriggeringState = s;
233                     logEvent(s, lastResetState, increasing);
234                     return action;
235                 }
236 
237                 /** {@inheritDoc} */
238                 @Override
239                 public SpacecraftState resetState(final EventDetector d, final SpacecraftState oldState) {
240                     if (lastTriggeringState != oldState) {
241                         lastTriggeringState = oldState;
242                         lastResetState = handler.resetState(getDetector(), oldState);
243                     }
244                     return lastResetState;
245                 }
246 
247             };
248         }
249 
250     }
251 
252 }