Software Architecture

The architecture of CSP.LMC is shared between the Controller and the Subarray. Both of them communicate with three sub-systems: CBF, PSS and PST. The Controller must also access the CSP.LMC Subarrays and the Capabilities device manager to report the information on the resources.

In the figure below, the C&C view of CSP.LMC controller is provided. The case of CSP.LMC subarray is identical, where Subsystem’s Controller are substituted with correspondant Subsystem’s subarrays and no other CSP.LMC subarrays are controlled. Further diagrams and a more comprehensive description of its component can be found at this page.

../_images/ControllerDiagram.jpg

The main operations of CSP are carried out into the three sub-elements. The interaction between the CSP Controller and the subordinate sub-systems devices are mediated through a Python class that works as a proxy (Component Class). This approach has the advantage of abstraction.

Since version 0.11.0 the state machine of ska-tango-base is no longer used. The motivation of this choice is described here. For this reason, custom state models are implemented for the Operational, Observing and Health State (CSP State Models).

Interface to subsystem TANGO devices

Specific operations on a sub-element can be done by specializing the proxy class for each sub-system and the corresponding functions are maintained in a specific part of the code.

Csp sub-system Component class

The component class is a mediator between CSP.LMC and a Subsystem Device. It acts as an adapter and allows, when needed, to execute specific instructions on a subsystem before invoking the required command. In other words, its functionalities are:

  • read and write of associated device’s attributes;

  • command execution;

  • subscription of attributes on the corresponding Tango Device.

Connector class

Connector Class is class working as interface to the TANGO system. It relies on TangoClient class of ska-tmc-common package developed by NCRA team, and it has the purpose to communicate with the device proxy of Sub System TANGO device for all the functionalities used by the Component classe.

One of the main advantage to have this class, it the possibility to be easily mocked during the tests.

Commands execution

A command issued on the CSP Controller or CSP Subarray by a TANGO client breaks up, nearly always, into several commands (>=3), that are forwarded to the connected CSP sub-systems (CBF, CSP, PST, PSS).

In order to avoid confusion in the nomenclature, given the multiple meanings of the word ‘command’, the following naming has been estabilished in the CSP.LMC codebase:

  • MainTask: the command issued from a client towards the CSP.LMC system. It is usually broken up into several tasks.

  • Task: the object that incapsulates the logic of a command (name, type, callbacks, etc.). It can be further broke up in different subtasks (Task or LeafTask) in a recursive structure.

  • LeafTask: the last Task in a task-tree structure, i.e. the smallest unit of execution. It can be either a command issued on a SubSystem device or an internal CSP.LMC command.

The commands execution architecture is capable to support any combination of the following execution types: Parallel, Sequential, CSP.LMC Internal and TANGO Device. Hence, for example, a MainTask can be broke up in a several sets of parallel tasks which will be issued on the relative sub-systems.

The CSP Controller or Subarray TANGO device has to be able to invoke the command on a sub-element and monitors its execution, detecting its progress and its final status (success/failure).

The sequence of operations to be performed are the following:

  • check the initial device state to determine if the command is allowed;

  • decompose the client command (MainTask) into its task-tree structure;

  • forward and execute the commands (i.e. Task, LeafTask) following their execution type (see Command_Map example below);

  • wait for the final status (the one expected after the end of successful execution) and detect possible conditions of failures;

  • implement support for timeout;

  • report the end of the command.

The execution of a command, is tracked and reported by the LRC attributes, leveraging the use of the BaseClasses API ‘invoke_lrc’ in order to obtain a more coherent and decoupled architecture. In particular, the command results are handled by the following elements: [TaskStatus, (ResultCode, message)].

The TaskStatus adheres to its ska-tango-base associated state machine and monitors the progress of the command (i.e. MainTask), while the ResultCode is mainly used to catch queuing, exceptions, aborts and other events during the command execution.

Moreover, since a MainTask is an aggregation of more than one Tasks, a basic implementation of the aggregation of multiple TaskStatus and ResultCode is in place (detailed info can be found at this link).

In the figure below an example data flow diagram of a CSP command is provided.

../_images/Command_Dataflow.jpg

In the next figure, an UML class diagram of the commands package is provided.

../_images/CommandsClassDiagram.png

Further diagrams and a more comprehensive description of the commands architecture can be found at this confluence page.

Command Map

A specific Python class (CommandMap) is used as a knowledge base to list the structure of all CSP.LMC commands, defining all the sub-tasks in which a main task can be divided into. The definition is implemented as a JSON string in order to be human readable and easily updated.

Commands are defined for both Controller and Subarray classes, including various attributes that are used for their execution management, like ‘type’ and ‘allowed_states’. Even CSP.LMC internal commands are defined in the map, whereas the ‘command_name’ parameter corresponds to the method signature within CSP.LMC codebase.

The main benefit of using the CommandMap class is the decoupling between command definition and implementation, allowing the developer to update the command structure without modifying the underlying code.

Here is an example of a CommandMap’s dictionary regarding the Common domain and the Controller device:

Command Map - Common repository - Controller device
 "on": {
     "type": "parallel",
     "allowed_states": {
         "attr_name": "state",
         "attr_value": [
             DevState.OFF,
             DevState.STANDBY,
             DevState.UNKNOWN,
         ],
     },
     "tasks": {
         "csp_subs": {"command_name": "on"},
         "pst": {"command_name": "on"},
         "pss": {"command_name": "on"},
         "cbf": {"command_name": "on"},
     },
 },
 "off": {
     "type": "parallel",
     "allowed_states": {
         "attr_name": "state",
         "attr_value": [
             DevState.ON,
             DevState.STANDBY,
             DevState.UNKNOWN,
         ],
     },
     "tasks": {
         "csp_subs": {"command_name": "off"},
         "pst": {"command_name": "off"},
         "pss": {"command_name": "off"},
         "cbf": {"command_name": "off"},
     },
 },
 "standby": {},
 "reset": {
     "type": "parallel",
     "allowed_states": {
         "attr_name": "state",
         "attr_value": [
             DevState.OFF,
             DevState.ON,
             DevState.STANDBY,
             DevState.UNKNOWN,
             DevState.FAULT,
         ],
     },
     "tasks": {
         "csp_subs": {"command_name": "reset"},
         "pst": {"command_name": "reset"},
         "pss": {"command_name": "reset"},
         "cbf": {"command_name": "reset"},
     },
 }

MainTask Composer

The MainTaskComposer Python class is used to build a tree-like structure of Task and LeafTask from the definition contained in the command_map, and from additional information present in the device Component Manager.

The MainTaskComposer includes the logic to build different Task objects, both depending on their execution type (internal, device, etc) and on specific implementation based on the command destination’s sub-system (CBF, PSS, PST).

Task Tracker

The TaskTracker Python class is responsible for tracking the completion of the sub-tasks within the MainTask. It reports the completion of a task through a callback which adheres to the ska-tango-base architecture. In details:

  • it monitors the task completion;

  • it signals via callback the task completion;

  • it updates the task and its attributes when the task status changes;

  • it aggregates TaskStatus, ResultCode and messages.

MainTask Executor

The MainTaskExecutor Python class is an asynchronous executor of tasks, responsible for unpacking the MainTask into Task/LeafTask and executing them. It can perform both parallel and serial tasks, while delivering to other modules the execution of CSP.LMC internal tasks and to the DeviceExecutor the fulfilling of TANGO device tasks. In details:

  • it is instantiated in the CSP device Component Manager;

  • it uses a ThreadPool to execute tasks in parallel;

  • it unwraps the parallel and serial tasks, recursively submitting them to the parallel or sequential handler until the final task is a LeafTask;

  • it employs the ska-tango-base ‘invoke_lrc’ method to execute a TANGO command on a subsystem device;

  • it manages the command timeout value;

  • it forwards the abort event if occurs.