Event Detection

The Moxie Engine uses specific types to produce the data required for event detection. BooleanValue, TimeDuration, and TimeInstant correspond to change events, relative time events, and absolute time events, respectively.

SysML Type Description
Moxie-Base::Structure::BooleanValue Used in change events and guards
Moxie-Base::Structure::TimeDuration Used in relative time events
Moxie-Base::Structure::TimeInstant Used in absolute time events

Times

When modeling physics, time precision is extremely important. Internally, Moxie uses the MoxieTime class for handling time calculations. The MoxieTime class contains a representation of the time based on input and exposes arithmetic enabling you to add and subtract durations of time accurately and precisely. Whether you provide time data as an ISO8601 string, a Gregorian calendar date and time, or a Julian date, MoxieTime will accept and process that information effectively, including handling any historical leap seconds that may occur during the time spans of the simulation. For more information on configuring leap seconds, see the com.agi.moxie.api.time.LeapSecondsProvider API documentation.

The instance of StkTimeHelper on StkToolbox provides methods such as stkAgVariantToMoxieTime and moxieTimeToStkEpochSeconds for converting the AgVariant and EpochSeconds values used in STK to the MoxieTime type used inside the Moxie Engine.

Because the Epoch used inside STK may be different from the start time of the simulation, the measure of EpochSeconds in STK may or may not correspond to the elapsed seconds from the start of the simulation. When using STK Object Model, take care to use the correct values, depending on which units you are using for time in STK.

Changes

For handling time-varying boolean expressions for change events (or for guards), the expression must result in a BooleanValue or a boolean primitive (SysML::Libraries::PrimitiveValueTypes::Boolean). Moxie reevaluates boolean primitives after every state update to determine whether or not to fire an event. There are several different ways to handle these boolean types:

  • For values that are constant, and therefore do not fire change events, you should use BooleanValue.constant(boolean).
  • For a property that is just a toggle set by other operations, you can use either a boolean primitive or a BooleanValue. These would be backed in by a BasicProperty<Boolean>(timeProvider, boolean) or a BasicProperty<BooleanValue>(timeProvider, BooleanValue.constant(boolean)) respectively in the delegate code.
  • For time-varying analysis, you can perform the event detection yourself and create a new instance using BooleanValue.fromTrueIntervals(Collection<Interval<MoxieTime>>).
  • For STK Desktop/Engine, you can use the StkToolbox to convert an instance of IAgStkAccess directly to a BooleanValue.
  • For STK Components, you can use any AccessQuery to compute the true intervals, and then you can convert those into a BooleanValue. For example:
    TimeIntervalCollection accessResult = accessQuery.getEvaluator().evaluate(analysisStartJulianDate, analysisStopJulianDate).getSatisfactionIntervals();
    List<Interval<MoxieTime>> moxieIntervals = accessResult.getSatisfactionIntervals().stream().map(x-> {
        JulianDate intervalStart = x.getStart().toTimeStandard(TimeStandard.getInternationalAtomicTime());
        JulianDate intervalStop = x.getStop().toTimeStandard(TimeStandard.getInternationalAtomicTime());
        return new Interval<>(
            MoxieTime.fromJulianTimeTai(intervalStart.getDay(), intervalStart.getSecondsOfDay()),
            MoxieTime.fromJulianTimeTai(intervalStop.getDay(), intervalStop.getSecondsOfDay()),
            true,
            false);
    }).collect(Collectors.toList());
    BooleanValue whenTrue = BooleanValue.fromTrueIntervals(moxieIntervals);

    Figure 1: Example code to extract a BooleanValue using STK Components

    When converting STK Components' representations of JulianDate into the Moxie equivalent JulianTime, make sure to specify a consistent time standard across STK Components and Moxie. To avoid any confusion over leap seconds between the two systems, International Atomic Time (TAI) is a good choice, as shown in Figure 1.

The instances of a Property type returned from an xyzProperty() method are intended to be unique to each object and should not be held or returned from any other object than the one that created them. xyzProperty().getValue() may return different values at different times. As the simulation progresses, the instances returned from the Property<T> methods may have been reconfigured with new instances and new values. So, be careful when caching or holding on to values from other objects. In general, the values returned from object properties are only valid at the current simulation time, even though they may represent dynamics that model values from the past and into the future. Unless explicitly documented otherwise, assume that the object references from calling getValue() or the return values from operations will change between calls to your code.

The Moxie API contains the infrastructure required to implement or extend delegates for use with Moxie.

You can use operations in state machines to trigger call events. Any time the Moxie Engine executes an operation from an effect in a state machine, it calls the corresponding Java method. If there is a trigger expecting a call event for that operation, the Java method must send the call event back to itself to alert the state machine that the operation has been called. Otherwise, unless the @AutoDelegateImplementation annotation is applied on the class, the call events will not fire. You can see an example of this in Figure 2, where we're using call events from the hot air balloon ride example.

@Override
public void launch() {
    mStateMachineRemoteControl.sendCallEventToSelf("Structure::BalloonOperator::launch");
}

@Override
public void land() {
    mStateMachineRemoteControl.sendCallEventToSelf("Structure::BalloonOperator::land");
}

Figure 2: The balloon operator call events for launching and landing the balloon

Manually implementing a delegate class requires explicit implementation of all call events. However, when using the @AutoDelegateImplementation annotation, sending call events is handled automatically. See Auto Implementation for more details.