Sequencer Command Service
A Sequencer can send commands to Assemblies and HCDs and Sequences to other Sequencers. In order to interact or send commands from one Sequencer to the other, one needs to create a Sequencer
instance first. The API provided by a Sequencer Command Service is tailored to Sequencer functionality.
Sequencer
First a Sequencer instance is needed. To create a Sequencer instance, the following parameters need to be passed to the Sequencer method:
subsystem
: Subsystem of the Sequencer to be resolved and sent commands (for eg. TCS, IRIS)obsMode
: observing mode of the Sequencer to be resolved and send commands to (for eg. wfos_imaging, wfos_spec)defaultTimeout
: optional max timeout to wait for completion of Sequences sent withsumbitAndWait
orqueryFinal
. The default value for this option is set to 10 hours since it will be common that the handling of Sequences can take a long time, and we don’t want unexpected timeouts to occur in production. For development, it might make sense to set this to some smaller value. This can always be overridden in the specificsubmitAndWait
andqueryFinal
calls, when appropriate (see below).
Here is an example:
- Kotlin
-
source
// create a sequencer entity val wfos = Sequencer(WFOS, ObsMode("wfos_darknight"))
Here is one showing the setting of the default timeout:
- Kotlin
-
source
// create a sequencer entity with a timeout val wfos2 = Sequencer(WFOS, ObsMode("wfos_darknight"), Duration.minutes(5))
Since all the components in the TMT architecture are dynamic in nature, which implies they can be shutdown and spawned dynamically on some other location, the Sequencer is resolved each time the Command Service DSL is used. It is possible to create a Sequencer entity for a non-existent component, but a command to the component will fail because the component is resolved when the command is sent.
Submitting Sequences to a Sequencer & Querying the Response
Creating a Sequence
Unlike Assemblies and HCDs, Sequencers send Sequences to other Sequencers. A Sequence
is a list of SequenceCommand
type instances, each of which can be one of the Setup
, Observe
or Wait
types. To create a Sequence, create individual SequenceCommand
objects and then create a Sequence
with the sequenceOf
DSL method as shown below.
- Kotlin
-
source
val wfosCommand1: SequenceCommand = Setup("ESW.wfos_darknight", "wfosCommand1") val wfosCommand2: SequenceCommand = Setup("ESW.wfos_darknight", "wfosCommand2") val sequence: Sequence = sequenceOf(wfosCommand1, wfosCommand2)
This example Sequence
consists of two steps. The Sequencer sends the two step Sequence
to the destination Sequencer and waits for it to complete, which means both of the two commands/steps are executed and completed. All Sequence
steps must complete successfully for the Sequence
to complete successfully.
The API for Sequence is here. The API for SequenceCommand is here
Submit a Sequence to a Sequencer
In order to send a Sequence
to another Sequencer, you can use the submit
or submitAndWait
DSL method as shown in examples below. The submit
DSL method sends the Sequence and returns Started
if the Sequence
is started or Invalid
if there is a reason it cannot be started. The query
and queryFinal
DSL is provided to check the response of a submitted sequence.
- Kotlin
-
source
val submitResponse: SubmitResponse = wfos.submit(sequence) val queryResponse: SubmitResponse = wfos.query(submitResponse.runId())
The query
DSL method allows checking on the state of the Sequence
. query
returns the current response immediately, which could be either a final response (eg. Completed
) or the Started
response. The runId
of the submitted Sequence
can be obtained from the SubmitResponse
returned by submit
. query
is useful in the case where polling of the command is needed, or the script needs to take other actions and periodically check for the completion of the Sequence
.
Note that if the runId
is not present in the Sequencer or has been removed from the CRM, the response returned is an Invalid
response with an IdNotAvailableIssue
.
Please refer to SubmitResponse extension utilities for using helper methods on SubmitResponse
.
By default, any negative SubmitResponse
(for e.g. Invalid
or Error
) is treated as a Script error. Refer to error handling section for more details. Alternatively, if you do not want to escalate a negative SubmitResponse
, you can use submit
Api with resumeOnError
flag.
- Kotlin
-
source
val response: SubmitResponse = wfos.submit(sequence, resumeOnError = true)
QueryFinal
While query
returns immediately, queryFinal
will wait for the final response of the Sequence
for the defaultTimeout
specified at the time of creation of the Sequencer
instance.
- Kotlin
-
source
val finalResponse: SubmitResponse = wfos.queryFinal(submitResponse.runId())
If you want to increase/decrease the defaultTimeout
, you can use the other variation of the same queryFinal
DSL method which takes a timeout.
- Kotlin
-
source
val finalRes: SubmitResponse = wfos.queryFinal(submitResponse.runId(), Duration.seconds(5))
Sending a Sequence with submitAndWait
The submitAndWait
DSL method is a combination of submit
followed with queryFinal
. If you are not interested in the initial response but only in final response of the Sequence, the submitAndWait
DSL method is more convenient. It submits the Sequence
and waits for the final response. If the Sequence
was successfully Started
, it will wait until the defaultTimeout
specified at the time of creation of the Sequencer
instance.
- Kotlin
-
source
val sequenceResponse: SubmitResponse = wfos.submitAndWait(sequence)
If you want to increase/decrease the default timeout, you can use the other variation of the same DSL method which takes a timeout.
- Kotlin
-
source
val sequenceRes: SubmitResponse = wfos.submitAndWait(sequence, Duration.seconds(5))
Handling Online and Offline
As with Assemblies and HCDs, a Sequencer can also indicate to another Sequencer to go offline or online using the Sequencer Command Service DSL.
Sequencer Sends goOnline
This DSL method is used to send online/offline commands to other sequencers. The Sequencer can go online only if it is currently in the offline state. If this command is received in any other state apart from offline, an Unhandled
response will be sent.
If the Sequencer is in the Offline state, and it receives the goOnline
command, the goOnline handlers of the receiving Sequencer will be called. In case the handlers fail, a GoOnlineHookFailed
response would be sent, and the Sequencer remains in the previous state. Otherwise, an Ok
response is returned, and the Sequencer goes in online(idle) state.
- Kotlin
-
source
val onlineResponse: GoOnlineResponse = wfos.goOnline()
Sequencer Sends goOffline
Go offline command is received in 2 states only.
- If the Sequencer is Idle, which means it is not processing any sequence currently
- If the Sequencer is Loaded with a sequence
If this command is sent in any other state apart from these, an Unhandled
response will be sent. If the Sequencer is in idle/loaded state, and it receives the goOffline
command, the goOffline handlers of the receiving Sequencer will be called. In case the handlers fail, a GoOfflineHookFailed
response would be sent, resulting the Sequencer remains in the previous state. Otherwise, an Ok
message is sent, and the Sequencer goes to offline state.
- Kotlin
-
source
val offlineResponse: GoOfflineResponse = wfos.goOffline()
Handling Diagnostic and Operations Mode
Diagnostic Mode
The diagnosticMode
command can be sent to Sequencers in all states and a DiagnosticModeResponse
is returned. If the Sequencer script has defined the diagnostic mode handlers, they will be called. If the handlers execute successfully, an Ok
response is sent; otherwise, the DiagnosticHookFailed
response is sent.
- Kotlin
-
source
val diagnosticModeResponse: DiagnosticModeResponse = wfos.diagnosticMode(utcTimeNow(), "engineering")
Operations Mode
The operationsMode
command returns a Sequencer in a diagnostic data mode to normal operation. operationsMode
is accepted by Sequencers in all states and an OperationsModeResponse
is returned to the sender. If the Sequencer has defined its operations mode handlers, they will be called. If the handlers execute successfully, an Ok
response is sent; otherwise, the OperationsHookFailed
response is sent.
- Kotlin
-
source
val operationsModeResponse: OperationsModeResponse = wfos.operationsMode()
Aborting and Stopping a Sequence
Stopping and aborting are meant to handle early termination of observing sequences in instruments. Aborting indicates that the current observe should stop as soon as possible and save the data if possible. Stop indicates that the current observe should stop at the end of the current step. In both cases, the observation is over and no subsequent steps can be executed.
Stop and abort commands are accepted only if the Sequencer is in Running
state, which means it is executing a sequence. If this command is sent in any other state, an Unhandled
response is returned. In all other cases, an Ok
response is sent.
Aborting a Sequence
On receiving the abort command in the Running
state, the Sequencer will execute the abort sequence handlers and on completion of execution of handlers (whether successful or failed), the Sequencer will discard all the pending
steps and return an Ok
response.
Note that, abort of a sequence does not abruptly terminate the in-flight step(s) which are already under execution. It will discard only the pending steps, and the sequence is finished gracefully after the inflight step(s) are finished although the script can take action to end the current step immediately.
- Kotlin
-
source
val abortResponse: OkOrUnhandledResponse = wfos.abortSequence()
Stopping a Sequence
Stopping a Sequence is very similar to aborting. The only difference is that instead of abort handlers, the stop handlers are called.
- Kotlin
-
source
val stopResponse: OkOrUnhandledResponse = wfos.stop()