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