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.
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))))
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.
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))))
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.
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)
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.
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:
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)
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());