Events

Events are the most basic type of asynchronous notification in TMT when an activity occurs somewhere in the TMT system and other components need to be notified. Each type of event has a unique purpose and unique information, but they all share same structural features. All events have EventInfo and a ParameterSet.

Note

The csw-params library offers out of the box support to serialize Events using Cbor, so that events can be produced and consumed by JVM (Java virtual machine) as well as Non-JVM applications.

For more on Cbor, refer to the technical doc.

EventTime

Each event includes its time of creation in UTC format. You can access that eventTime as follows:

Scala
val source    = Prefix("iris.filter.wheel")
val eventName = EventName("temperatures")
val event     = SystemEvent(source, eventName)

// accessing eventTime
val eventTime = event.eventTime
Java

//apply returns current time in UTC UTCTime now = UTCTime.now(); //using constructor UTCTime anHourAgo = new UTCTime(Instant.now().minusSeconds(3600)); //return current time in UTC UTCTime currentTime = UTCTime.now(); //some past time using utility function UTCTime aDayAgo = new UTCTime(Instant.now().minusSeconds(86400));

System Event

SystemEvent is the type used to describe the majority of events in the system. An example is a demand that is the output of an algorithm in one component that is used as an input to another. SystemEvent is also used to publish internal state or status values of a component that may be of interest to other components in the system.

Scala
//keys
val k1: Key[Int]    = KeyType.IntKey.make("encoder")
val k2: Key[Int]    = KeyType.IntKey.make("speed")
val k3: Key[String] = KeyType.StringKey.make("filter")
val k4: Key[Int]    = KeyType.IntKey.make("notUsed")

//prefixes
val ck1   = Prefix("wfos.red.filter")
val name1 = EventName("filterWheel")
val ck3   = Prefix("iris.imager.filter")
val name3 = EventName("status")

//parameters
val p1: Parameter[Int]    = k1.set(22)
val p2: Parameter[Int]    = k2.set(44)
val p3: Parameter[String] = k3.set("A", "B", "C", "D")

//Create SystemEvent using madd
val se1: SystemEvent = SystemEvent(ck1, name1).madd(p1, p2)
//Create SystemEvent using apply
val se2: SystemEvent = SystemEvent(ck3, name3, Set(p1, p2))
//Create SystemEvent and use add
val se3: SystemEvent = SystemEvent(ck3, name3).add(p1).add(p2).add(p3)

//access keys
val k1Exists: Boolean = se1.exists(k1) //true

//access Parameters
val p4: Option[Parameter[Int]] = se1.get(k1)

//access values
val v1: Array[Int] = se1(k1).values
val v2: Array[Int] = se2.parameter(k2).values
//k4 is missing
val missingKeys: Set[String] = se3.missingKeys(k1, k2, k3, k4)

//remove keys
val se4: SystemEvent = se3.remove(k3)

//add more than one parameters, using madd
val se5: SystemEvent = se4.madd(k3.set("X", "Y", "Z").withUnits(Units.day), k4.set(99, 100))
val paramSize: Int   = se5.size

//update existing key with set
val se6: SystemEvent = se5.add(k2.set(5, 6, 7, 8))
Java
//keys
Key<Integer> k1 = JKeyType.IntKey().make("encoder", JUnits.encoder);
Key<Integer> k2 = JKeyType.IntKey().make("speed");
Key<String> k3 = JKeyType.StringKey().make("filter");
Key<Integer> k4 = JKeyType.IntKey().make("notUsed");

//prefixes
Prefix prefix1 = Prefix.apply(JSubsystem.WFOS, "red.filter");
EventName name1 = new EventName("filterWheel");
Prefix prefix2 = Prefix.apply(JSubsystem.IRIS, "imager.filter");
EventName name2 = new EventName("status");

//parameters
Parameter<Integer> p1 = k1.set(22);
Parameter<Integer> p2 = k2.set(44);
Parameter<String> p3 = k3.set("A", "B", "C", "D");

//Create SystemEvent using madd
SystemEvent se1 = new SystemEvent(prefix1, name1).madd(p1, p2);
//Create SystemEvent using add
SystemEvent se2 = new SystemEvent(prefix2, name2).add(p1).add(p2);
//Create SystemEvent and use add
SystemEvent se3 = new SystemEvent(prefix2, name2).add(p1).add(p2).add(p3);

//access keys
boolean k1Exists = se1.exists(k1); //true

//access Parameters
Optional<Parameter<Integer>> p4 = se1.jGet(k1);

//access values
List<Integer> v1 = se1.jGet(k1).orElseThrow().jValues();
List<Integer> v2 = se2.parameter(k2).jValues();
//k4 is missing
Set<String> missingKeys = se3.jMissingKeys(k1, k2, k3, k4);

//remove keys
SystemEvent se4 = se3.remove(k3);

Observe Event

ObserveEvent are standardized events used to describe an activities within the data acquisition process. These events are typically published by Science Detector Assemblies, which emit ObserveEvents during their exposures to signal the occurrence of specific activities/actions during the acquisition of data.

Note

The example ObserveEvents do not match the descriptions of the ESW Phase 1 review. The model files and documents can be used to create standard ObserveEvents.

Scala
//keys
val k1: Key[Int]    = KeyType.IntKey.make("readoutsCompleted")
val k2: Key[Int]    = KeyType.IntKey.make("coaddsCompleted")
val k3: Key[String] = KeyType.StringKey.make("fileID")
val k4: Key[Int]    = KeyType.IntKey.make("notUsed")

//prefixes
val ck1   = Prefix("iris.ifu.detectorAssembly")
val name1 = EventName("readoutEnd")
val ck3   = Prefix("wfos.red.detector")
val name3 = EventName("exposureStarted")

//parameters
val p1: Parameter[Int]    = k1.set(4)
val p2: Parameter[Int]    = k2.set(2)
val p3: Parameter[String] = k3.set("WFOS-RED-0001")

//Create ObserveEvent using madd
val se1: ObserveEvent = ObserveEvent(ck1, name1).madd(p1, p2)
//Create ObserveEvent using apply
val se2: ObserveEvent = ObserveEvent(ck3, name3, Set(p1, p2))
//Create ObserveEvent and use add
val se3: ObserveEvent = ObserveEvent(ck3, name3).add(p1).add(p2).add(p3)

//access keys
val k1Exists: Boolean = se1.exists(k1) //true

//access Parameters
val p4: Option[Parameter[Int]] = se1.get(k1)

//access values
val v1: Array[Int] = se1(k1).values
val v2: Array[Int] = se2.parameter(k2).values
//k4 is missing
val missingKeys: Set[String] = se3.missingKeys(k1, k2, k3, k4)

//remove keys
val se4: ObserveEvent = se3.remove(k3)
Java
//keys
Key<Integer> k1 = JKeyType.IntKey().make("readoutsCompleted", JUnits.NoUnits);
Key<Integer> k2 = JKeyType.IntKey().make("coaddsCompleted", JUnits.NoUnits);
Key<String> k3 = JKeyType.StringKey().make("fileID");
Key<Integer> k4 = JKeyType.IntKey().make("notUsed");

//prefixes
Prefix prefix1 = Prefix.apply(JSubsystem.IRIS, "ifu.detectorAssembly");
EventName name1 = new EventName("readoutEnd");
Prefix prefix2 = Prefix.apply(JSubsystem.WFOS, "red.detector");
EventName name2 = new EventName("exposureStarted");

//parameters
Parameter<Integer> p1 = k1.set(4);
Parameter<Integer> p2 = k2.set(2);
Parameter<String> p3 = k3.set("WFOS()-RED-0001");

//Create ObserveEvent using madd
ObserveEvent oc1 = new ObserveEvent(prefix1, name1).madd(p1, p2);
//Create ObserveEvent using add
ObserveEvent oc2 = new ObserveEvent(prefix2, name2).add(p1).add(p2);
//Create ObserveEvent and use add
ObserveEvent oc3 = new ObserveEvent(prefix2, name2).add(p1).add(p2).add(p3);

//access keys
boolean k1Exists = oc1.exists(k1); //true

//access Parameters
Optional<Parameter<Integer>> p4 = oc1.jGet(k1);

//access values
List<Integer> v1 = oc1.jGet(k1).orElseThrow().jValues();
List<Integer> v2 = oc2.parameter(k2).jValues();
//k4 is missing
Set<String> missingKeys = oc3.jMissingKeys(k1, k2, k3, k4);

//remove keys
ObserveEvent oc4 = oc3.remove(k3);

JSON Serialization

Events can be serialized to JSON. The library has provided JsonSupport helper class and methods to serialize Status, Observe and System events.

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

//key
val k1: Key[MatrixData[Double]] = DoubleMatrixKey.make("myMatrix")

val name1  = EventName("correctionInfo")
val prefix = Prefix("aoesw.rpg")

//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 i1: Parameter[MatrixData[Double]] = k1.set(m1)
//events
val observeEvent: ObserveEvent = ObserveEvent(prefix, name1).add(i1)
val systemEvent: SystemEvent   = SystemEvent(prefix, name1).add(i1)

//json support - write
val observeJson: JsValue = JsonSupport.writeEvent(observeEvent)
val systemJson: JsValue  = JsonSupport.writeEvent(systemEvent)

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

//construct command from string
val systemEventFromPrettyStr: SystemEvent = JsonSupport.readEvent[SystemEvent](Json.parse(str))

//json support - read
val observeEvent1: ObserveEvent = JsonSupport.readEvent[ObserveEvent](observeJson)
val systemEvent1: SystemEvent   = JsonSupport.readEvent[SystemEvent](systemJson)
Java
//key
Key<MatrixData<Double>> k1 = JKeyType.DoubleMatrixKey().make("myMatrix");

//prefixes
Prefix prefix1 = Prefix.apply(JSubsystem.AOESW, "rpg");
EventName name1 = new EventName("correctionInfo");

//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);


//events
ObserveEvent observeEvent = new ObserveEvent(prefix1, name1).add(i1);
SystemEvent systemEvent = new SystemEvent(prefix1, name1).add(i1);

//json support - write
JsValue observeJson = JavaJsonSupport.writeEvent(observeEvent);
JsValue systemJson = JavaJsonSupport.writeEvent(systemEvent);

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

//construct DemandState from string
SystemEvent statusFromPrettyStr = JavaJsonSupport.readEvent(Json.parse(str));

//json support - read
ObserveEvent observeEvent1 = JavaJsonSupport.readEvent(observeJson);
SystemEvent systemEvent1 = JavaJsonSupport.readEvent(systemJson);

Unique Key Constraint

By choice, a ParameterSet in either ObserveEvent or SystemEvent event 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
//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")

val name1 = EventName("filterWheel")

//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)
//StatusEvent with duplicate key via constructor
val systemEvent =
  SystemEvent(prefix, name1, Set(encParam1, encParam2, encParam3, filterParam1, filterParam2, filterParam3))
//four duplicate keys are removed; now contains one Encoder and one Filter key
val uniqueKeys1 = systemEvent.paramSet.toList.map(_.keyName)

//try adding duplicate keys via add + madd
val changedStatusEvent = systemEvent
  .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 = systemEvent.madd(Set(miscParam1, encParam1))
//now contains encoderKey, filterKey, miscKey
val uniqueKeys3 = finalStatusEvent.paramSet.toList.map(_.keyName)
Java
//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 prefix1 = Prefix.apply(JSubsystem.WFOS, "blue.filter");
EventName name1 = new EventName("filterWheel");

//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);

//StatusEvent with duplicate key via madd
SystemEvent event = new SystemEvent(prefix1, name1).madd(
        encParam1,
        encParam2,
        encParam3,
        filterParam1,
        filterParam2,
        filterParam3);
//four duplicate keys are removed; now contains one Encoder and one Filter key
Set<String> uniqueKeys1 = event.jParamSet().stream().map(Parameter::keyName).collect(Collectors.toUnmodifiableSet());

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

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