################################## Mid CSP LMC Tango Clients Examples ################################## .. _csp-lmc-example-page: In the following sections some itango interface examples are provided, to be easily applied to a generic python client. Basic assumptions for each example are: 1. the system has been fresh initialized 2. the only CSP sub-system deployed is the CBF 3. CBF supports up to 4 VCCs and 4 FSPs 4. all TANGO operations (read/write/command_inout) are always successful. No check on the results is done in the following examples. 5. PST can be added to the deployment but only On/Off command are available. To control CSP with itango, a proxy to CSP Controller and Subarrays has to be created: :: csp_ctrl = tango.DeviceProxy('mid-csp/control/0') csp_sub1 = tango.DeviceProxy('mid-csp/subarray/01') csp_sub2 = tango.DeviceProxy('mid-csp/subarray/02') csp_sub3 = tango.DeviceProxy('mid-csp/subarray/03') It is possible also to create proxies to CBF controller and subarrays, as well as to PST Beam, in order to check their states and mode after a command is issued:: cbf_ctrl = tango.DeviceProxy('mid_csp_cbf/sub_elt/controller') cbf_sub1 = tango.DeviceProxy('mid_csp_cbf/sub_elt/subarray_01') cbf_sub2 = tango.DeviceProxy('mid_csp_cbf/sub_elt/subarray_02') cbf_sub3 = tango.DeviceProxy('mid_csp_cbf/sub_elt/subarray_03') pst_beam1 = tango.DeviceProxy("mid-pst/beam/01") To monitor the VCCs and FSPs, it is possible to create a proxies to the CSP LMC Capabilities: :: fsp_cap = tango.DeviceProxy('mid-csp/capability-fsp/0') vcc_cap = tango.DeviceProxy('mid-csp/capability-vcc/0') Please note that all commands on subarray follow the `ObsState state model `_ defined in ADR-8. A CSP.LMC Subarray doesn't allow commands from observing states other than those specified in this model. After the deployment, all the Mid CSP.LMC devices a part from the capabilities are in following conditions: :: - state DISABLE. - healthState UNKNOWN - adminMode OFFLINE - obstState EMPTY (subarrays) The capabilities start in ON state. From now on, all the examples refers to these proxy objects. For Subarray commands, the proxy to subarray 1 will be used. ******************************* Mid CSP.LMC start communication ******************************* Starting from MID CSP.LMC version 1.1.0, the CSP LMC devices start with adminMode = ONLINE. The Mid CSP.LMC Controller propagates the *adminMode* value to all its Subarray devices and other subordinate systems. As a result, communication with the subsystems is enabled automatically at startup. Before setting the *adminMode* to *ENGINEERING* or *ONLINE* on the CBF Controller, the CSP Controller first forwards its *cbfSimulationMode* attribute to the CBF Controller *simulationMode*. This attribute defaults to True, but it can be changed by the user before setting *adminMode*. Note: the Mid CBF only accepts updates to *simulationMode* when its *adminMode* is *OFFLINE*. Simulation mode can also be modified at any time during operation by writing directly to the *cbfSimulationMode* attribute. The communication with the subsystems can be manually initiated setting the *adminMode* to *ENGINEERING* or *ONLINE*, using one of the following commands:: csp_ctrl.adminMode = 2 #set to ENGINEERING or :: csp_ctrl.adminMode = 0 #set to ONLINE At startup, all devices will be in the following states:: - state ON (subarrays), OFF (CSP and CBF controllers and PST) - healthState UNKNOWN - adminMode ENGINEERING(2)/ONLINE(0) - obsState EMPTY (subarrays), IDLE (PST) .. _csp-lmc-initSysParam: ************************ Configure DishID-VCC map ************************ Before turning ON the CSP LMC, it is mandatory to issue the LoadDishCfg command to the controller. It requires as input a JSON string that could contains the DishId-VCC map itself or the CAR URI at which the map could be found (:ref:`here `. ):: csp_ctrl.loadDishCfg(json_string) The command returns immediately the following list: :: [array([2], dtype=int32), ['1679401117.9451234_224758016395799_loadDishCfg']] In case of failure, to highlight the result of the command, the attribute *alarmDishIdCfg* is raised on the CSP.LMC Controller. Moreover, the health state of all the deployed CSP.LMC Subarrays is forced to FAILED since they are not able to work without the proper dishId-VCC map:: csp_ctrl.alarmDishIdCfg = True csp_sub1.healthState = HelathState.FAILED csp_sub2.healthState = HealthState.FAILED csp_sub3.healthState = HealthSate.FAILED As soon as the command *LoadDishCfg* is executed with success, the alarn flag is reset and each CSP.LMC Subarray re-evalauate its health state:: csp_ctrl.alarmDishIdCfg = False csp_sub1.healthState = OK csp_sub2.healthState = OK csp_sub3.healthState = OK It is now possible to invoke the power-on command. ********************************** Power-on (off) the Mid.CSP ********************************** To power on the Mid.CSP devices, issue the command:: csp_ctrl.On([]) # empty list: power on all the available sub-systems (CBF, PSS, PST) or :: csp_ctrl.On(['mid_csp_cbf/sub_elt/controller', ]) # power-on only the specified sub-systems The command returns immediately the following list: :: [array([2], dtype=int32), ['1679401117.9451234_224758016395799_On']] Where: - 2 is the command status (2 means QUEUED) - '1679401117.9451234_224758016395799_On' is a unique id assigned to the command. It can be used to track the execution status of the command It is possible to read the command result state using: :: cmd_result = csp_ctrl.commandResult *cmd_result* is a Tuple of two strings: - the first element is the name of last executed CSP task - the second one is the result code: allowed values for the result code are defined in SKA Base Classes module `ska_tango_base.commands `_ Possible results for the current example are: - ('on', '0') # *On* task completed successfully - ('on', '1') # *On* task started - ('on', '3') # *On* task completed with failure Some of the long running command attributes can also be accessed. Long running command result can be read using: :: long_running_command_result = csp_ctrl.longRunningCommandResult *long_running_command_result* is a Tuple of a string and a list: - the first element is a command id assigned when command is invoked - the second element is a list of result code (matching the value in command result attribute described above) and result message Possible results: - ('1684312814.139426_265125881596693_On', '[0, "Task on completed with result OK"]') # *On* task completed successfully on one devices - ('1684312814.139426_265125881596693_Configure', '[0, "Task configure completed with result OK"]') # *Configure* task completed successfully on two devices - ('1684312814.139426_265125881596693_Off', '[3, "cbf-controller: Task off_cbf failed with exception: Unexpected result code for off command: 3"]') # *Off* task completed with failure on one device It is also possible to access long running command status at the various stages of command execution. This attribute can be read using: :: long_running_command_status = csp_ctrl.longRunningCommandStatus *long_running_command_status* is a Tuple of pairs of strings. In each pair the elements represent the following: - the first element is a command id assigned when command is invoked - the second element is the task status: allowed values for the task status are defined in SKA Base Classes module `ska_tango_base.executor `_ Possible results: - ('1684312814.139426_265125881596693_On', 'QUEUED') - ('1684312814.139426_265125881596693_On', 'IN_PROGRESS') - ('1684312814.139426_265125881596693_On', 'COMPLETED') - ('1684312814.139426_265125881596693_On', 'ABORTED') - ('1684312814.139426_265125881596693_On', 'FAILED') - ('1684312814.139426_265125881596693_On', 'REJECTED') Note that there can be more than one pair of command id and task status in the attribute, if there are more commands invoked. The command *On* invoked on the Mid.CSP Controller is forwarded to the Mid CBF sub-system Controller and to all the Mid.CSP Subarrays. This can be checked by the state of all controllers and subarrays:: csp_ctrl.state() -> ON csp_sub1.state() -> ON csp_sub2.state() -> ON csp_sub3.state() -> ON cbf_ctrl.state() -> ON cbf_sub1.state() -> ON cbf_sub2.state() -> ON cbf_sub3.state() -> ON pst_beam1.state() -> ON The same logic and syntax apply also for *Off* commands. ****************************************** Assign resources to a Mid CSP.LMC Subarray ****************************************** To assign resources to a subarray both the subarray and controller devices must be in ON operational state. To move the system in such a state, please follow the previous example. Please note that right now only the receptor ids SKA001, SKA036, SKA103, SKA104 can be assigned to a subarray. The following JSON string can be used to assign receptors to the Mid CSP.LMC:: json_string = '{"subarray_id": 1,"dish":{ "receptor_ids":["SKA001", "SKA036"]}}' Note that PST beams cannot be assigned yet. On successfully assignment, the receptors SKA001 and SKA036 are affiliated to subarray 1. Invoke the *AssignResources* command on Mid.CSP Subarray 1:: csp_sub1.assignresources(json_string) If command is successful, the command result will report:: csp_sub1.commandResult -> ('assignresources', '0') csp_sub1.commandResultName -> 'assignresources' csp_sub1.commandResultCode -> '0' The receptors assigned to Mid.CSP Subarray 1 are:: csp_sub1.assignedReceptors -> ("SKA001", "SKA036") Information about resources availability are provided by the Mid.CSP Controller. To get the list of all the receptors:: csp_ctrl.receptorsList -> ("SKA001", "SKA036", "SKA103", "SKA104") To get the list of the available receptors:: csp_ctrl.unassignedReceptorIDs -> ("SKA103", "SKA104") To get the affiliation of the receptors to the subarrays:: csp_ctrl.receptorMembership -> [1, 1, 0, 0] After resource allocation the Mid CSP.LMC and Mid CBF Subarray *obsState* attribute value changes from EMPTY to IDLE. To check the observing state of the devices:: csp_sub1.obsstate -> IDLE cbf_sub1.obsstate -> IDLE ******************************** Configure, issue and end a scan ******************************** After a Subarray has resources assigned, it is possible to configure it and then start a scan. The ``json_string`` to be used for configure and scan can be found :ref:`here `. They have to be assigned to a variable and sent as command input as showed above for assignresources. First of all, Configure command has to be issued:: csp_sub1.configure(json_string_configure) The observimg state will be in CONFIGURING during the execution. After that, if the command is successful:: csp_sub1.commandResult -> ('configure', '0') csp_sub1.obsstate -> READY cbf_sub1.obsstate -> READY The subarray in READY observing state can be re-configured with a new configuration that overwrites the previous one. When the subarray is READY, a scan can be started, issuing the *Scan* command:: csp_sub1.scan(json_string_scan) If the command is successful:: csp_sub1.commandResult -> ('scan', '1') csp_sub1.obsstate -> SCANNING cbf_sub1.obsstate -> SCANNING Note that the result code associated to the *Scan* command will remain '1' for all the duration of the scanning process. In fact, according to `ADR-8 `_ a scan can be interrupted by the *EndScan* or the *Abort* command. The *Abort* command has to be intended as an emergency call that interrupts abruptly the scan process. On the other side, the *EndScan* first ensures that all the processes are correctly managed. To end a scan, just issue:: csp_sub1.endscan() After *EndScan* is successful, the subarray *obsState* is READY, and another scan can be issued with the same configuration. On the other side, if the scan is aborted, the *obsState* will go (after a short time in ABORTING) to ABORTED state To perform a new scanning, the subarray observation should be restarted (via the *ObsReset* command) and a new configuration need to be sent (`ADR-8 `_) The sequence of operation is:: csp_sub1.abort() csp_sub1.commandResult -> ('abort', '1') csp_sub1.obsstate -> ABORTING csp_sub1.commandResult -> ('abort', '0') csp_sub1.obsstate -> ABORTED csp_sub1.obsreset() csp_sub1.commandResult -> ('obsreset', '1') csp_sub1.obsstate -> RESETTING csp_sub1.commandResult -> ('obsreset', '0') csp_sub1.obsstate -> IDLE ******************************** Go To Idle and Release Resources ******************************** The resources of a subarray can only be released when its obsState is IDLE. When the subarray is in READY (as happens after the end of a scan) it must first be sent to IDLE with the command:: csp_sub1.gotoidle() Upon successful completion of the command, the *obsState* will be IDLE and the resources can be partially or totally removed from the subarray. To partially remove some of the allocated resources, a json string, like the one used for assign resources (see above) should be sent. This string must specify the receptors to be removed. Following the previous example, to remove the receptor 1 from the list of the assigned receptors to the Mid CSP.LMC Subarray 1:: json_string = '{"subarray_id": 1,"dish":{ "receptor_ids":["SKA001"]}} csp_sub1.ReleaseResources(json_string) On command success, the subarray will have only receptor 2 assigned to it, and its *obsState* will stay in IDLE. The released receptor (1) will now appear in the pool of the Mid CSP.LMC available resources. This can be verified, accessing the proper attributes of the Mid CSP.LMC Subarray and Controller devices:: csp_sub1.commandResult -> ('releaseresources', '0') csp_sub1.obsstate -> IDLE csp_sub1.assignedReceptors -> ["SKA036"] csp_ctrl.receptorsList -> ["SKA001", "SKA036", "SKA103", "SKA104"] csp_ctrl.unassignedReceptorIDs -> ["SKA001", "SKA103", "SKA104"] csp_ctrl.receptorMembership -> [0, 1, 0, 0] Otherwise, if all resources are meant to be removed, this can be done with the *ReleaseAllResouces* command:: csp_sub1.ReleaseAllResources() On command success, the subarray will be EMPTY again:: csp_sub1.commandResult -> ('releaseallresources', '0') csp_sub1.obsstate -> EMPTY csp_sub1.assignedReceptors -> [] csp_ctrl.unassignedReceptorIDs -> ["SKA001", "SKA036", "SKA103", "SKA104"] csp_ctrl.receptorMembership -> [0, 0, 0, 0] ********************************** Standby the Mid.CSP ********************************** The *Standby* command is rejected by the CSP LMC since all the CSP subsystems do not support it. Invoking the command on the controller: :: csp_ctrl.Standby([]) will return the following: :: [array([5], dtype=int32), ['standby command rejected, as it is unimplemented for CSP subsystems.']] ***************************************************** Connect to the FSP Processing Modes Capability Device ***************************************************** To connect to the capability device:: fsp_cap = tango.DeviceProxy('mid-csp/capability-fsp/0') fsp_cap.state() = DevState.ON After Controller power-up:: fsp_cap.fspAvailable = [1,2,3,4] After configuring Mid CSP Subarray 1 for correlation using FSP1 and FSP2:: fsp_cap.fspCorrelation = [1] fsp_cap.fspFunctionMode = ['CORR', 'CORR', 'IDLE', 'IDLE'] fsp_cap.Available = 2 ***************************************************** Connect to the VCC Capability Device ***************************************************** To connect to the capability device:: vcc_cap = tango.DeviceProxy('mid-csp/capability-vcc/0') The default state is On, since it is a monitoring device:: vcc_cap.state() Then, it is possible to verify some parameters like:: vcc_cap.versionId vcc_cap.vccFqdn vcc_cap.vccsDeployed