State Variables

State variables are used when an Assembly wants to track the status of a command sent to an HCD using a matcher. For more information, see Publishing State.

A state represents some aspect of a component’s internal state. There are two types called CurrentState and DemandState. They both share the same structural features. All state variables have Prefix and ParameterSet.

The PubSub feature of the HCD provides CurrentState values to the PubSub subscriber.

DemandState

A state variable that indicates the demand or requested state.

Scala
source// prefix
val prefix = Prefix("wfos.prog.cloudcover")

// key
val charKey: Key[Char]       = KeyType.CharKey.make("charKey")
val intKey: Key[Int]         = KeyType.IntKey.make("intKey")
val booleanKey: Key[Boolean] = KeyType.BooleanKey.make("booleanKey")
val utcTimeKey: Key[UTCTime] = KeyType.UTCTimeKey.make("utcTimeKey")
val notUsedKey: Key[String]  = KeyType.StringKey.make("notUsed")

// parameters
val charParam: Parameter[Char]       = charKey.set('A', 'B', 'C').withUnits(NoUnits)
val intParam: Parameter[Int]         = intKey.set(1, 2, 3).withUnits(meter)
val booleanParam: Parameter[Boolean] = booleanKey.set(true, false)
val utcTime: Parameter[UTCTime]      = utcTimeKey.set(UTCTime.now())

// create DemandState and use sequential add
val ds1: DemandState = DemandState(prefix, StateName("testStateName")).add(charParam).add(intParam)
// create DemandState and add more than one Parameters using madd
val ds2: DemandState = DemandState(prefix, StateName("testStateName")).madd(intParam, booleanParam)
// create DemandState using apply
val ds3: DemandState = DemandState(prefix, StateName("testStateName"), Set(utcTime))

// access keys
val charKeyExists: Boolean = ds1.exists(charKey) // true

// access Parameters
val p1: Option[Parameter[Int]] = ds1.get(intKey)

// access values
val v1: Array[Char]    = ds1(charKey).values
val v2: Array[Boolean] = ds2.parameter(booleanKey).values
val missingKeys: Set[String] = ds3.missingKeys(
  charKey,
  intKey,
  booleanKey,
  utcTimeKey,
  notUsedKey
)

// remove keys
val ds4: DemandState = ds3.remove(utcTimeKey)

// update existing keys - set it back by an hour
val ds5: DemandState = ds3.add(utcTimeKey.set(UTCTime(UTCTime.now().value.minusSeconds(3600))))
Java
source//prefix
Prefix prefix = Prefix.apply(JSubsystem.WFOS, "prog.cloudcover");

//keys
Key<Character> charKey = JKeyType.CharKey().make("charKey");
Key<Integer> intKey = JKeyType.IntKey().make("intKey");
Key<Boolean> booleanKey = JKeyType.BooleanKey().make("booleanKey");
Key<UTCTime> utcTimeKey = JKeyType.UTCTimeKey().make("utcTimeKey");
Key<String> notUsedKey = JKeyType.StringKey().make("notUsed");


ObsId obsId = ObsId.apply("2020A-001-123");

//parameters
Parameter<Character> charParam = charKey.set('A', 'B', 'C').withUnits(NoUnits);
Parameter<Integer> intParam = intKey.set(1, 2, 3).withUnits(meter);
Parameter<Boolean> booleanParam = booleanKey.set(true, false);
Parameter<UTCTime> timestamp = utcTimeKey.set(UTCTime.now());

//create DemandState and use sequential add
DemandState ds1 = new DemandState(prefix, new StateName("testStateName")).add(charParam).add(intParam);
//create DemandState and add more than one Parameters using madd
DemandState ds2 = new DemandState(prefix, new StateName("testStateName")).madd(intParam, booleanParam);
//create DemandState using apply
DemandState ds3 = new DemandState(prefix, new StateName("testStateName")).add(timestamp);

//access keys
boolean charKeyExists = ds1.exists(charKey); //true

//access Parameters
Optional<Parameter<Integer>> p1 = ds1.jGet(intKey);

//access values
List<Character> v1 = ds1.jGet(charKey).orElseThrow().jValues();
List<Boolean> v2 = ds2.parameter(booleanKey).jValues();
Set<String> missingKeys = ds3.jMissingKeys(charKey,
        intKey,
        booleanKey,
        utcTimeKey,
        notUsedKey);

//remove keys
DemandState ds4 = ds3.remove(utcTimeKey);

//update existing keys - set it back by an hour
DemandState ds5 = ds3.add(utcTimeKey.set(new UTCTime(UTCTime.now().value().minusSeconds(3600))));

CurrentState

A state variable that is published by a component that describes its internal state. Used by Assemblies to determine command completion in Command Service.

Scala
source
// prefix val prefix = Prefix("wfos.prog.cloudcover") // key val charKey = KeyType.CharKey.make("charKey") val intKey = KeyType.IntKey.make("intKey") val booleanKey = KeyType.BooleanKey.make("booleanKey") val utcTimeKey = KeyType.UTCTimeKey.make("utcTimeKey") val notUsedKey = KeyType.StringKey.make("notUsed") // parameters val charParam = charKey.set('A', 'B', 'C').withUnits(NoUnits) val intParam = intKey.set(1, 2, 3).withUnits(meter) val booleanParam = booleanKey.set(true, false) val utcTime = utcTimeKey.set(UTCTime.now()) // create CurrentState and use sequential add val cs1 = CurrentState(prefix, StateName("testStateName")).add(charParam).add(intParam) // create CurrentState and add more than one Parameters using madd val cs2 = CurrentState(prefix, StateName("testStateName")).madd(intParam, booleanParam) // create CurrentState using apply val cs3 = CurrentState(prefix, StateName("testStateName"), Set(utcTime)) // access keys val charKeyExists = cs1.exists(charKey) // true // access Parameters val p1: Option[Parameter[Int]] = cs1.get(intKey) // access values val v1: Array[Char] = cs1(charKey).values val v2: Array[Boolean] = cs2.parameter(booleanKey).values val missingKeys: Set[String] = cs3.missingKeys( charKey, intKey, booleanKey, utcTimeKey, notUsedKey ) // remove keys val cs4 = cs3.remove(utcTimeKey) // update existing keys - set it back by an hour val cs5 = cs3.add(utcTimeKey.set(UTCTime(UTCTime.now().value.minusSeconds(3600))))
Java
source//prefix
Prefix prefix = Prefix.apply(JSubsystem.WFOS, "prog.cloudcover");

//keys
Key<Character> charKey = JKeyType.CharKey().make("charKey");
Key<Integer> intKey = JKeyType.IntKey().make("intKey");
Key<Boolean> booleanKey = JKeyType.BooleanKey().make("booleanKey");
Key<UTCTime> timestampKey = JKeyType.UTCTimeKey().make("timestampKey");
Key<String> notUsedKey = JKeyType.StringKey().make("notUsed");


ObsId obsId = ObsId.apply("2020A-001-123");

//parameters
Parameter<Character> charParam = charKey.set('A', 'B', 'C').withUnits(NoUnits);
Parameter<Integer> intParam = intKey.set(1, 2, 3).withUnits(meter);
Parameter<Boolean> booleanParam = booleanKey.set(true, false);
Parameter<UTCTime> timestamp = timestampKey.set(UTCTime.now());

//create CurrentState and use sequential add
CurrentState cs1 = new CurrentState(prefix, new StateName("testStateName")).add(charParam).add(intParam);
//create CurrentState and add more than one Parameters using madd
CurrentState cs2 = new CurrentState(prefix, new StateName("testStateName")).madd(intParam, booleanParam);
//create CurrentState using apply
CurrentState cs3 = new CurrentState(prefix, new StateName("testStateName")).add(timestamp);

//access keys
boolean charKeyExists = cs1.exists(charKey); //true

//access Parameters
Optional<Parameter<Integer>> p1 = cs1.jGet(intKey);

//access values
List<Character> v1 = cs1.jGet(charKey).orElseThrow().jValues();
List<Boolean> v2 = cs2.parameter(booleanKey).jValues();
Set<String> missingKeys = cs3.jMissingKeys(charKey,
        intKey,
        booleanKey,
        timestampKey,
        notUsedKey);

//remove keys
CurrentState cs4 = cs3.remove(timestampKey);

//update existing keys - set it back by an hour
CurrentState cs5 = cs3.add(timestampKey.set(new UTCTime(UTCTime.now().value().minusSeconds(3600))));

JSON Serialization

State variables can be serialized to JSON. The library has provided JsonSupport helper class and methods to serialize DemandState and CurrentState.

Scala
sourceimport play.api.libs.json.{JsValue, Json}

// key
val k1: Key[MatrixData[Double]] = DoubleMatrixKey.make("myMatrix")
// values
val m1: MatrixData[Double] = MatrixData.fromArrays(
  Array(1.0, 2.0, 3.0),
  Array(4.1, 5.1, 6.1),
  Array(7.2, 8.2, 9.2)
)

// parameter
val p1: Parameter[MatrixData[Double]] = k1.set(m1)

// state variables
val ds: DemandState  = DemandState(Prefix("wfos.blue.filter"), StateName("testStateName")).add(p1)
val cs: CurrentState = CurrentState(Prefix("wfos.blue.filter"), StateName("testStateName")).add(p1)

// json support - write
val dsJson: JsValue = JsonSupport.writeStateVariable(ds)
val csJson: JsValue = JsonSupport.writeStateVariable(cs)

// optionally prettify
val str: String = Json.prettyPrint(dsJson)

// construct command from string
val scFromPrettyStr = JsonSupport.readStateVariable[DemandState](Json.parse(str))

// json support - read
val ds1: DemandState  = JsonSupport.readStateVariable[DemandState](dsJson)
val cs1: CurrentState = JsonSupport.readStateVariable[CurrentState](csJson)
Java
source//key
Key<MatrixData<Double>> k1 = JKeyType.DoubleMatrixKey().make("myMatrix");

//values
Double[][] doubles = {{1.0, 2.0, 3.0}, {4.1, 5.1, 6.1}, {7.2, 8.2, 9.2}};
MatrixData<Double> m1 = MatrixData.fromArrays(doubles);

//parameter
Parameter<MatrixData<Double>> i1 = k1.set(m1);

//state variables
DemandState ds = new DemandState(Prefix.apply(JSubsystem.WFOS, "blue.filter"), new StateName("testStateName")).add(i1);
CurrentState cs = new CurrentState(Prefix.apply(JSubsystem.WFOS, "blue.filter"), new StateName("testStateName")).add(i1);

//json support - write
JsValue dsJson = JavaJsonSupport.writeStateVariable(ds);
JsValue csJson = JavaJsonSupport.writeStateVariable(cs);

//optionally prettify
String str = Json.prettyPrint(dsJson);

//construct DemandState from string
DemandState dsFromPrettyStr = JavaJsonSupport.readStateVariable(Json.parse(str));

//json support - read
DemandState ds1 = JavaJsonSupport.readStateVariable(dsJson);
CurrentState cs1 = JavaJsonSupport.readStateVariable(csJson);

Unique Key Constraint

By design, a ParameterSet in either DemandState or CurrentState will be optimized to store only unique keys. When using add or madd methods on events to add new parameters, if the parameter being added has a key which is already present in the paramSet, the already stored parameter will be replaced by the given parameter.

Note

If the Set is created by component developers and given directly while creating an event, then it will be the responsibility of component developers to maintain uniqueness with parameters based on key.

Here are some examples that illustrate this point:

Scala
source// keys
val encoderKey: Key[Int] = KeyType.IntKey.make("encoder")
val filterKey: Key[Int]  = KeyType.IntKey.make("filter")
val miscKey: Key[Int]    = KeyType.IntKey.make("misc.")

// prefix
val prefix = Prefix("wfos.blue.filter")

// params
val encParam1 = encoderKey.set(1)
val encParam2 = encoderKey.set(2)
val encParam3 = encoderKey.set(3)

val filterParam1 = filterKey.set(1)
val filterParam2 = filterKey.set(2)
val filterParam3 = filterKey.set(3)

val miscParam1 = miscKey.set(100)

// DemandState with duplicate key via constructor
val statusEvent = DemandState(
  prefix,
  StateName("testStateName"),
  Set(encParam1, encParam2, encParam3, filterParam1, filterParam2, filterParam3)
)
// four duplicate keys are removed; now contains one Encoder and one Filter key
val uniqueKeys1 = statusEvent.paramSet.toList.map(_.keyName)

// try adding duplicate keys via add + madd
val changedStatusEvent = statusEvent
  .add(encParam3)
  .madd(
    filterParam1,
    filterParam2,
    filterParam3
  )
// duplicate keys will not be added. Should contain one Encoder and one Filter key
val uniqueKeys2 = changedStatusEvent.paramSet.toList.map(_.keyName)

// miscKey(unique) will be added; encoderKey(duplicate) will not be added
val finalStatusEvent = statusEvent.madd(Set(miscParam1, encParam1))
// now contains encoderKey, filterKey, miscKey
val uniqueKeys3 = finalStatusEvent.paramSet.toList.map(_.keyName)
Java
source//keys
Key<Integer> encoderKey = JKeyType.IntKey().make("encoder", JUnits.encoder);
Key<Integer> filterKey = JKeyType.IntKey().make("filter");
Key<Integer> miscKey = JKeyType.IntKey().make("misc.");

//prefix
Prefix prefix = Prefix.apply(JSubsystem.WFOS, "blue.filter");

//params
Parameter<Integer> encParam1 = encoderKey.set(1);
Parameter<Integer> encParam2 = encoderKey.set(2);
Parameter<Integer> encParam3 = encoderKey.set(3);

Parameter<Integer> filterParam1 = filterKey.set(1);
Parameter<Integer> filterParam2 = filterKey.set(2);
Parameter<Integer> filterParam3 = filterKey.set(3);

Parameter<Integer> miscParam1 = miscKey.set(100);

//Demand state with duplicate key via madd
DemandState state = new DemandState(prefix, new StateName("testStateName")).madd(
        encParam1,
        encParam2,
        encParam3,
        filterParam1,
        filterParam2,
        filterParam3);
//four duplicate keys are removed; now contains one Encoder and one Filter key
Set<String> uniqueKeys1 = state.jParamSet().stream().map(Parameter::keyName).collect(Collectors.toUnmodifiableSet());

//try adding duplicate keys via add + madd
DemandState changedState = state.add(encParam3).madd(filterParam1, filterParam2, filterParam3);
//duplicate keys will not be added. Should contain one Encoder and one Filter key
Set<String> uniqueKeys2 = changedState.jParamSet().stream().map(Parameter::keyName).collect(Collectors.toUnmodifiableSet());

//miscKey(unique) will be added; encoderKey(duplicate) will not be added
DemandState finalState = changedState.madd(miscParam1, encParam1);
//now contains encoderKey, filterKey, miscKey
Set<String> uniqueKeys3 = finalState.jParamSet().stream().map(Parameter::keyName).collect(Collectors.toUnmodifiableSet());

Source Code for Examples