The previous sections have described the conceptual steps in the framework development process. We now illustrate them through the concrete case of the Attitude and Orbit Control System (AOCS) framework which we developed under a research and development contract with the European Space Agency. Its architecture is fully documented in Pasetti (2001), where there is also a description of the AOCS domain. The framework source code can be obtained from the project web site[4] which also provides a guided tour through the framework together with the complete set of cookbook recipes for adapting the AOCS framework. The AOCS framework is implemented in C++. So the following description relies on C++ code fragments.
[4] http://www.SoftwareResearch.net/AOCSFrameworkProject/ProjectHomePage.html
AOCS is usually the most complex subsystem on board a satellite. Its task is to ensure that the satellite attitude and orbit remain stable and follow profiles prespecified by ground control. In the case of geostationary telecommunication satellites, for instance, AOCS is responsible for ensuring that each particular satellite retains its position over the Earth's equator at a given longitude, and that it keeps its antennas pointed toward the ground station.
The conceptual structure of an AOCS is shown in Figure 7.5. AOCS is a typical embedded hard real-time control system. Its chief task is to periodically collect measurements from a set of sensors and convert them into commands for a set of actuators. AOCS interacts with a ground control station from which it receives commands (telecommands), and to which it forwards housekeeping data (telemetry).
Like all satellite systems, AOCS must remain fully operational in the event of any single failure and must survive prolonged periods of ground station outage. Robustness to faults and autonomy require AOCS to perform failure detection and failure recovery actions.
It is clearly not possible to cover all the aspects of the AOCS framework in this chapter. The following subsections concentrate on two AOCS functionalities (out of thirteen overall) and on how they were modeled by the framework. The emphasis is on showing how the methodological concepts presented in the first part of this chapter can help the framework design process.
An AOCS typically contains several control loops serving such diverse purposes as stabilizing the attitude of the satellite, stabilizing its orbital position, controlling the execution of slews for turning around a satellite, and managing the satellite internal angular momentum.
The objects implementing these control loops are called controllers. They tend to implement the same flow of activities starting with the acquisition and filtering of measurements from sensors, continuing with a computation, and ending with the application of commands to actuators designed to counteract any deviation of the variable under control (e.g. the satellite attitude) from its desired value. Despite the ubiquity of closed-loop controllers and the similarities in their structure, existing AOCS applications have not developed any abstractions to represent them. Controllers are normally implemented by one-of-a-kind objects that hardcode the control algorithm. The management of controllers is not recognized as an explicit and separate activity. This lack of domain-wide abstractions is a good example of the project-oriented design that was criticized by Meyer (1989).
The first step in the design of a solution for the controller functionality in the AOCS framework therefore lies in the realization that a controller abstraction is needed. This abstraction must be characterized with the help of a domain expert who defines the generic operations that can be performed on a controller. The aCiRC cards can be used for this purpose. Initially, the controller abstraction can be represented by an abstract interface (see Figure 7.6).
All objects representing closed-loop controllers must implement the interface Controllable. It recognizes two methods. The doControl method directs the object to acquire the sensor measurements, to detect discrepancies from the desired value, and to compute and apply the commands for the actuators. Since closed-loop controllers can become unstable, an additional method, isStable, is provided to ask a controller to check its own stability.
Although the implementation of the control algorithms is application-specific (each controller on each satellite has its own control algorithm), there are some types of control algorithms that recur often. It therefore makes sense for the framework to provide default implementations of interface Controllable that can be configured to provide those common control algorithms. Application developers will then have the choice of providing their own entirely new implementation, of using a default implementation, or of modifying a default implementation by subclassing it and overriding selected methods. The framework will offer a set of objects implementing the same interface Controllable thus forming a class family.
The next step in the design of the controller part of the AOCS framework is the identification and analysis of the variation points. One such variation point was implicitly identified by the controller abstraction the control algorithm, as encapsulated by an object implementing the interface Controllable, is a point where flexibility is required because different AOCS applications use different control algorithms.
A second variation point arises from the observation that, for the same control loop, an AOCS may need different control algorithms at different stages of a mission. Consider, for instance, the control of a satellite's attitude. Immediately after the satellite has been ejected from its launcher, control algorithms are required that are capable of stabilizing the satellite even in the presence of the high angular rates that are typically induced by the ejection mechanism. Later in the mission, after the satellite has been brought to a near standstill, the emphasis is on very fine control (sometimes to an accuracy of fractions of an arcsecond) and more sensitive control algorithms are required. Therefore an AOCS controller typically operates in different modes corresponding to different operational conditions. Each mode has its own set of control algorithms. Switching from one mode to the next is often done autonomously by the satellite, according to criteria that vary from mission to mission. The set of modes and the mode-switching logic together represent another framework variation point.
After the variation points have been fully characterized and documented with the help of domain experts, it is necessary to identify architectural solutions to implement the flexibility that they require. This is where domain-specific design patterns, which are created to model adaptability, are introduced. Our experience suggests that the designer should start by analyzing the design patterns in a standard catalog, such as the GoF catalog (Gamma et al., 1995), and then proceed to refine them so as to tailor them to a specific domain. The objective is to build a repository of domain-specific design patterns. Good design patterns are hard to find and we believe that, just as software engineers have long been in the habit of maintaining libraries of subroutines and components, they should learn to do the same for design patterns. Frameworks are one vehicle through which such repositories can be built and made available across projects and design teams.
Let us now apply this procedure to the first controller variation point. An analysis of its characteristics points us to a modified form of the Separation construction principle. A controller manager object is introduced that is responsible for all the controllers in an application. It holds a list of objects of type Controllable and, when it is activated, it asks them to check their stability and, if the stability is confirmed, to perform their allotted control action. This results in the UML-F diagram shown in Figure 7.7.
Consider now the second variation point, namely the dependency on operational mode. To each operational mode, a list of controllable objects is associated. The controller manager should switch from one list to another depending on operational conditions. The mode-switching logic, however, is a point of variation and therefore should be abstracted out of the controller manager to make it possible for applications to modify it easily. One solution is to define a mode-manager interface (see Figure 7.8).
A controller mode manager holds multiple lists of controllable objects one list for each operational mode. At any given time, there is one active list, which is returned by getList. The active list is updated by calling updateList. The latter method implements the mode-switching logic and represents the mode variation point.[5]
[5] If the ControllerModeManager is to change controller modes autonomously, then each time the getList method is invoked, it can call the updateList method to determine which list is active, based on some external conditions prior to returning the ControllableList. Another option would be for the updateList method to be called by some external object to trigger a change in controller mode.
Concrete mode managers implementing concrete mode-switching logic are implemented as subclasses of ControllerModeManager. The resulting solution for the controller functionality is shown in Figure 7.9.
This solution to the mode-dependency problem is loosely based on the Abstract Factory design pattern (Gamma et. al., 1995) (the mode manager acts as an abstract factory) with the important differences that the controller manager interrogates the mode manager every time it is activated, not just at initialization, and that the mode manager dynamically decides which list of controllers to supply on the basis of current operational conditions. Note that although the mode-switching logic is application-dependent, here too, as in the case of the controllable objects, it is possible to recognize some standard implementations that the framework can provide as predefined implementations of interface ControllerModeManager, thus giving rise to a second class family.
The proposed solution separates the implementation of the control algorithms (in the controllable objects) from their management (in the controller manager), and from the mode-switching logic (in the mode manager). The controller manager is an application-independent component, whereas the controller and the controller mode-manager abstractions generate two class families. These two families, together with the controller manager, form a class team. As it stands, our solution requires other parts of the AOCS framework to interact with several different objects the mode manager, the controller objects, and the controller manager. This interaction can be simplified by embedding all the controller-related objects in an enclosing object that becomes the only interface between them and the rest of the AOCS application. This solution is based on the Fa ade design pattern (Gamma et. al., 1995) and is shown in an informal notation in Figure 7.10. The dashed lines with arrowheads represent references that the common interface has to its contained components. When the components are packaged as indicated by the dashed rounded rectangle in the diagram, the class team becomes a subsystem.
Thus, it is possible to recognize in the design solution for the controller functionality all the elements presented in the first part of this chapter. The solution is based on two abstractions, namely the controller and mode-manager abstractions, to which there correspond two major variation points. The objects modeling the controller functionality in an AOCS application can be encapsulated in a subsystem. Their classes form a class team containing two class families.
The design process is iterative, with a first iteration performed on the controller variation point and a second performed on the mode-management variation point. The design problem is attacked in the first instance by making use of standard design patterns. These patterns are then continually refined until a domain-specific design pattern is produced. This domain pattern acts as the conceptual glue that holds together the class families in the class team.
As a second example of framework design, we consider the development of a design solution to the problem of telemetry management. Telemetry data are the housekeeping data that a satellite must periodically send to its ground station to allow those on the ground to verify the correct functioning of the satellite. The data represent a subset of the state of the objects making up the AOCS software.
In current AOCS systems, telemetry processing is controlled by a so-called telemetry handler that collects telemetry data directly, formats and stores the data in a dedicated buffer, and then has the data transferred to the ground. To accomplish its task, the telemetry handler needs an intimate knowledge of the type and format of the telemetry data. It has to know which objects to collect data from, which data to collect from those objects, and how the collected data is to be formatted for transmission to the ground. It is this coupling between telemetry handler and telemetry data that makes the former application-specific and hinders its reuse. A symptom of this tight coupling is the lack, in conventional AOCS applications, of an abstraction for 'telemeterable' objects the software treats objects that need to send data to the telemetry stream on an individual basis, not as members of a group of like entities. This is another illustration of the project-oriented design style of conventional software.
As in the case of controller functionality, a framework-based solution for the telemetry problem begins with the identification of the basic domain abstraction. The first obvious abstraction is the telemetry stream, representing the data sink to which telemetry data are written. A telemetry stream acts as a proxy, within the AOCS software, for the channel through which telemetry data are forwarded to the ground station. It can be represented by the abstract class in Figure 7.11.
The TmStream interface provides a write method for each of the main data types used in the framework. For simplicity, the figure only shows two such methods. The UML-F tag ' ' is used to signify that not all methods are shown. Additionally, a flush method is provided for data streams where data are first buffered and then forwarded in chunks.
The second abstraction is perhaps more interesting because it introduces the concept of a telemeterable object, namely an object whose state can potentially be included in telemetry. To this abstraction is associated the abstract interface Telemeterable (see Figure 7.12) which defines the methods that may be performed on a telemeterable object.
The fundamental method is writeToTm(). Calling it causes an object to write its internal state to the telemetry stream. The method takes a telemetry stream as an argument because the object needs to know where the telemetry data should be written. The object will use the write methods provided by the telemetry stream interface to write selected portions of its internal state to the telemetry stream.
The next step in the design process is again the identification of the variation points. One obvious variation point is the telemetry stream itself different AOCS use different mechanisms for sending telemetry data to the ground. The second variation point is given by the format and content of the telemetry data that varies from mission to mission. A domain-specific design pattern is again needed to cope with these elements of variation. Figure 7.13 illustrates the pattern as a UML-F diagram.
A telemetry manager is introduced that holds a reference to the telemetry stream and to the list of telemeterable objects. When it is activated, it goes through the items in the list calling their writeToTm method and passing to them the currently valid telemetry stream.
The TmStream interface gives rise to a class family because the framework provides a handful of concrete implementations corresponding to commonly used telemetry streams. The telemeterable interface is more problematic this interface is typically implemented by objects that participate in other parts of the framework. The controller objects of the previous subsection, for instance, would normally be telemeterable objects since the state of the controllers is the kind of information that should be included in the telemetry data. Telemeterable is therefore a pure interface for which no implementation can be defined at framework level. A class family cannot be defined for it because, at the level of design defined so far, it is not possible to say which classes will implement it. This will become clear only when the design solution for the telemetry problem is merged with the design solutions for the other AOCS functionalities to produce a full framework architecture. At that point it might, for instance, be decided that controllers should be telemeterable objects, and this allows their inclusion in the telemeterable class family.
This difficulty in associating a class family to the Telemeterable interface will be taken up in Section 7.7 where we introduce the concept of framelets. For the time being, it is important to stress that this second example has again shown how a framework design solution can be articulated along the steps identified in the cluster cycle model.