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 aBasicProperty<Boolean>(timeProvider, boolean)
or aBasicProperty<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 ofIAgStkAccess
directly to aBooleanValue
. - For STK Components, you can use any
AccessQuery
to compute thetrue
intervals, and then you can convert those into aBooleanValue
. 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 equivalentJulianTime
, 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.