Enumerated and Derived Resources
In addition to our on-board camera, let's imagine that we also have an instrument on-board that is continuously collecting data, say a magnetometer, based on a data collection mode. Perhaps at especially interesting times in the mission, the magnetometer is placed in a high rate collection mode and at other times remains in a low rate collection mode. For our model, we want to be able to track the collection mode over time along with the associated data collection rate of that mode.
The first thing we'll do to accomplish this is create a Java enumeration called MagDataCollectionMode
that gives us the list of available collection modes along with a mapping of those modes to data collection rates using enum fields. We will also add a getter method to get the data rate based on the mode. Let's say that we have three modes, OFF
, LOW_RATE
, and HIGH_RATE
with values 0.0
, 500.0
, and 5000.0
, respectively. After coding this up, our enum should look like this:
package missionmodel;
public enum MagDataCollectionMode {
OFF(0.0), // kbps
LOW_RATE(500.0), // kbps
HIGH_RATE(5000.0); // kbps
private final double magDataRate;
MagDataCollectionMode(double magDataRate) {
this.magDataRate = magDataRate;
}
public double getDataRate() {
return magDataRate;
}
}
With our enumeration built, we can now add a couple of new resources to our DataModel
class. The first resource, which we'll call MagDataMode
, will track the current data collection mode for the magnetometer. Declare this resource as a discrete MutableResource
of type MagDataCollectionMode
public MutableResource<Discrete<MagDataCollectionMode>> MagDataMode;
and then add the following lines of code to the constructor to initialize the resource to OFF
and register it with the UI.
MagDataMode = resource(discrete(MagDataCollectionMode.OFF));
registrar.discrete("MagDataMode",MagDataMode, new EnumValueMapper<>(MagDataCollectionMode.class));
As you can see, declaring and defining this resource was not much different than when we built RecordingRate
except instead of referencing the Double
type, we are referencing our enumerated type MagDataCollectionMode
.
Another resource we can add is one to track the numerical value of the data collection rate of the magnetometer, which is based on the collection mode. In other words, we can derive the value of the rate from the mode. Since we are deriving this value and don't intend to emit effects directly onto this resource, we can declare it as a discrete Resource
of type Double
instead of a MutableResource
.
public Resource<Discrete<Double>> MagDataRate; // bps
When we go to define this resource in the constructor, we need to tell the resource to get its value by mapping the MagDataMode
to its corresponding rate.
A special static method in the DiscreteResourceMonad
class called map()
allows us to define a function that operates on the value of a resource to get a derived resource value.
In this case, that function is simply the getter function we added to the MagDataCollectionMode
. The resulting definition and registration code for MagDataRate
then becomes
MagDataRate = map(MagDataMode, MagDataCollectionMode::getDataRate);
registrar.discrete("MagDataRate", MagDataRate, new DoubleValueMapper());
If your IDE is struggling to find the map()
function, the associated import statement is
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteResourceMonad.map;
Instead of deriving a resource value from a function using map()
, there are a number of static methods in the DiscreteResources
class, which you can use to add()
, multiply()
, divide()
, etc. resources. For example, you could have a Total
resource that simple used add()
to sum some resources together.