This is the first in a series of three articles on how the UML can be used to develop real-time and embedded systems. This first article identifies the major notational and semantic features of the UML without a great deal of formalism. In parts II and III, the author goes through the development cycle of an actual application-an anesthesia patient ventilator-and shows how to express its analysis and design using the UML.

UML? Isn’t that a foreign car like the Yugo? I’m so glad you asked. The acronym stands for the Unified Modeling Lang-uage, a third-generation object-oriented (OO) modeling language. It represents a substantial effort from a large number of methodologists to construct a common means for describing complex systems using the now mature concepts of object orientation. Visually, it is reminiscent of Object Modeling Technique (OMT) in that rectangles represent classes, lines represent associations, and diamonds indicate aggregation, but the visual aspects of the notation are the least significant. The effort of defining the object and class semantics of the underlying UML metamodel was spearheaded by Grady Booch, Jim Rumbaugh, and Ivar Jacobson while the specification of state behavior is strongly based on David Harel’s work on statecharts. However, a great number of people worked on the evolution of the UML and contributed significantly. The UML is a cooperative effort from dozens of top methodologists and should not be thought of as merely the coalition of three or four individuals. Nor should you be put off by the "design by committee" approach taken. The UML presents the best and most useful set of object semantics from literally centuries of accumulated experience achieved in the last couple of decades.

As I’ve already mentioned, the UML is a language for the specification of complex systems. It does not include a development process per se. 1 Process refers to how you use the various features of UML and how and when you link them together during the course of system development. This article will (mostly) limit itself to showing what UML is and leave it to the remaining parts to show how it can be profitably applied.

The UML was originally developed in response to the Object Management Group’s (OMG) call for a proposal for a standardized object modeling language. There were other contenders, but given the rigor and robustness of the UML and its broad industry support, the competitors have fallen by the wayside. The goals of the UML are to:

  • Provide a common, expressive visual modeling language for capturing object model semantics
  • Provide a model with a formal and rigorous semantic basis that doesn’t require too much formalism by the user
  • Provide a metamodel which may be tailored and extended by the user
  • Be independent of programming language and development process
  • Encourage the growth and maturity of the OO tools market
  • Support the latest in methodological development, including collaborations, design patterns, large scale system development, frameworks, and components
  • Integrate the best practices culled from the last couple of decades of active OO development and research
    Why should Real-Time Embedded Developers Care?

    I could go on and on about the software development crisis, about how we need better ways of doing things, yadda, yadda, yadda.

    Now that’s all true. But the bottom line is that just as structured methods won the methodology wars of the ’70s because they allowed the development of larger systems with fewer defects in less time, the war between OO and structured methods is also over. It was won hands-down by the object methods and for exactly the same reasons. Software is getting bigger and more complex. It’s being used in more safety-critical applications where correctness is essential. Objects clearly are a superior method for decomposing complex systems. This is not to say that structured methods are bad-merely that OO methods are better. They provide better abstraction and encapsulation capabilities, better reuse facilities, better means for defining behavioral semantics including tasking and reactive state behavior, better means for capturing system requirements, and better means for mapping software to physical architectures.

    I’ve conducted informal surveys at the various conferences at which I speak and have found that while about 20% of embedded developers I talk to use object methods today, 50% to 60% are object-wanna-bes. 2 They just haven’t found the right tools or project. There are some others who are using structured methods now but aren’t yet convinced, and there are still others who "don’t need no stinking methodology." For the most part, however, the embedded developers I meet are anxiously awaiting the opportunity to use object methods on a project.

    The concerns I do hear voiced about using object methods in real-time systems are mostly about the speed and size of the deployed application. That is, "can objects be made efficient?" I have a number of thoughts on this topic.

    Yes, Objects are Efficient

    First, yes, objects can be just as efficient as functions. In one sense, objects are merely an organizing principle, just as functional decomposition is. Objects are not inherently less efficient than functions. Inefficiencies arise when inexperienced users don’t understand the implementation consequences of design decisions or of their selected programming language. C++ is a very efficient language if you know what you’re doing and can be very inefficient if you don’t. Andrew Koenig once said that there is no problem in software that can’t be solved by yet another level of indirection. I think he is almost right-it’s difficult to improve efficiency by adding additional levels of indirection and abstraction. However, objects don’t require additional levels of abstraction. They allow you to design systems that are more robust and portable through the proper application of abstraction, but you are not required to add inefficiencies in order to use objects. In fact, I find that objects allow you to identify inefficiencies and streamline the high bandwidth data paths within a system. Many developers have found that the better abstraction facilities in objects actually improve performance and decrease program size. Grady Booch reports that the original Booch components, some 200,000 lines of Ada, were successfully reimplemented in 20,000 lines of C++.

    Better Correct than Fast

    The industry had this argument in the ’70s between the guys who wanted to use high-level languages and the assembly language programmers. The structured language guys won clearly and decisively for the reasons I’ve mentioned. That is not to say that assembly language should never be used nor that some small applications wouldn’t benefit from being done entirely in assembly, but for the most part, spending time writing assembly code means dealing with the wrong set of abstractions ("Should I use direct or indirect indexed addressing?"). As much as possible, the software abstractions should occur directly and obviously in the problem domain rather than being layered on because of implementation concerns. Neverthe-less, performance and size are critical issues in real-time systems. In real-time systems, timeliness is one aspect of correctness. If a system doesn’t meet its deadlines, then it’s wrong. But with all other things being equal, I firmly believe it’s possible for experienced developers to write larger systems with fewer defects in less time using OO methods instead of structured methods.

    Objects May Be Implemented In Any Language

    Thirdly, object orientation is primarily a means to model systems. Object models can be implemented in any language, including assembly language. I myself have designed and implemented small 8-bit processor-based systems (20 to 60K of compiled code) that were entirely object-oriented in their structure, yet implemented using assembler. I have also had teams work for me that implemented object designs in C on CISC, RISC, and DSP processors. Such solutions are not always as satisfying as when the implementation language directly supports the modeling concepts (such as C++ or Ada 95), but it can still be done. Still, in my opinion, this resulted in better systems than would have been done with structured designs.

    The Bigger the System, the More You Need Objects

    For small, simple systems, OO methods give an improvement over structured methods. Although objects are certainly effective for the development of small embedded systems, the amount of benefit from using objects grows with the size of the system. I was recently the system architect for a relatively large medical system with 20 processors linked via a multi-mastered bus. This system had a distributed architecture with a custom real-time CORBA-like object repository (CORBAWE 3 ), a custom embedded real-time protocol, and thousands of safety-relevant requirements. The specification of state behavior alone was a 500-page document. The use of objects in this system greatly enhanced the stability and robustness of the system while permitting reusable components to be developed. Although I think we could have developed the system using structured methods, it would have been larger, more complex, had more defects, and would have taken longer to develop had we used more traditional approaches. As the system grows in size, the benefits reaped from using object decomposition grow as well.

    A Brief Look at UML

    We don’t have the space to deal with all the features in depth, but let’s do a brief look at the main ones. The major features of UML include:

  • Use cases and scenarios
  • Object and class models
  • Statecharts and other behavioral specifications
  • Large-scale structuring
  • Design patterns
  • Extensibility mechanisms
    Use Cases and Scenarios

    A use case is a broad area of functionality of a system, roughly equivalent to a function point. A use case is a kind of usage of the system in which external objects (called actors) interact with the system. In use case analysis, the system is treated as a black box, so that all use cases must be visible, in some sense, to the actors. A use case achieves a particular goal for the actors participating in the use case. Each use case is a generic description of how this system goal is achieved. Most real systems have a small set of use cases-usually anywhere from three to a couple dozen. For example, an ECG monitor has several use cases:

  • Monitor the cardiac function of a patient
  • Configure the monitor
  • Update the monitor’s software
  • Upload monitored data to the hospital information network (HIN)
  • Download information, such as lab results and patient demographics, from the HIN

    Use cases show the communication of the system with external actors in the performance of a system function. Use cases may associate with external actors and may use or extend another use case. When actors communicate with a use case, it means that the actors send or receive messages from the system. When one use case uses another, it means that the functionality of the former use case requires the behavior specified in the latter. A use case can extend another by doing a more specific kind of action. Figure 1 shows what a typical use case looks like.

    Figure 1

    Scenarios are instances of use cases. A scenario is a specific, step-by-step sequence of messages sent between the actors and the system. Normally, there are an infinite set of scenarios corresponding to a single use case. Every point in the use case where a different message can be sent or received constitutes a different scenario. It’s important to model a set of scenarios that span the behavior space of the use case. It is common to model a few dozen primary scenarios for each use case as well as many more for atypical cases, such as exception handling and error recovery.

    The UML provides two notations for showing scenarios: the sequence diagram and the collaboration diagram. The sequence diagram represents objects (shown as labeled vertical lines) sending messages (shown as horizontal or diagonal arrows). Time flows down the page, but normally only sequence is shown. The UML notation guide states that rulers and linear time may be added if desired. The activities shown on the sequence diagram may be annotated with text, and timing marks can be added to show exact timing relationships when desired. Figure 2 shows a simple scenario for the use of an elevator.

    Figure 2

    Collaboration diagrams show objects, associations, and messages. Collaboration diagrams are a more structural view of scenarios but provide essentially the same information. Figure 3 shows the same scenario as the previous figure, but using the collaboration diagram notation.

    Figure 3

    Object Models

    The object model is the key to an OO system. It defines the system structure by identifying objects and their classes and the relationships that exist between classes. An object is an entity that has identity and is unique from all other objects. A class, roughly speaking, is the type of an object and defines the properties of objects instantiated from it. For example, Sensor_1 and Sensor_2 might be identical devices. They are different objects but, being of the same class, have all the same properties. For example, the Sensor class might define attributes (data members or variables) such as CalibrationConstant and CurrentValue, and behaviors such as Calibrate() and getValue() . Although different objects can have different values stored in their own attributes, all objects of class Sensor will have these attributes and behaviors.

    Class diagrams contain primarily classes, objects, and their relationships. Classes are depicted in these diagrams as rectangles containing the name of the class. This rectangle may be compartmentalized with the class name in the first compartment, attributes in the second compartment, operations in the third, exceptions in the fourth, and so on. Object names are followed by a colon and are underlined. Most commonly, the class of the object is provided as well, following the colon, as in Figure 3.

    The UML defines several different kinds of relationships, as shown in Table 1.

    TABLE 1: UML Relationships.

    Relationship
    Description
    Notation

    Generalization
    One class is a more specialized version of another.
    Solid line arrow with a closed arrowhead pointing to the more general class

    Association
    One class uses the services of another.
    Solid line connecting the associated classes. Optional open arrowhead shows direction of navigation.

    Aggregation
    One class "owns" another.
    A special form of association. Unfilled diamond at the "owner" end of association

    Composition
    One class is composed of another.
    A special form of ag gregation. Shown either using a filled diamond at the "com posite" end or the composite graphically contains the "compo nent"

    Refinement
    One entity is a more refined version of anotherýused to show template instantiation.
    Same as generalization except that the line is dashed.

    Dependency
    One class depends on another.
    Dashed line with an open arrowhead

    Associations (including aggregation and composition) may have additional annotations of a label, role names, and role multiplicities. The label identifies the purpose of the association while the role names identify how the object at that end of the association participates in the association. Role multiplicities identify the number of objects that participate in the association. The UML uses the following notation for multiplicities:

    1 One object must participate
    0,1 At most one object may participate
    x..y Any number of objects may participate from x to y
    x Exactly x object(s) must participate
    * Zero or more object(s) may participate

    Figure 4 illustrates most of the features of class diagrams. Note that textual annotation may be added using either free-form text or using the note icon (look for the top right edge folded down).

    Figure 4

    Statecharts and Other Behavioral Specifications

    An important subset of all classes has behavior that can be modeled using finite state machines. Such classes are called reactive because they react in specific ways to incoming events. Reactive classes must always be in exactly one state. 4 When they’re constructed, they must enter a valid initial state, though this state may change based on its constructor’s parameters. My definition of a state is: a distinguishable, disjoint, orthogonal ontological condition that persists for a significant period of time.

    This means that a state is a condition of existence that is somehow distinguishable from other such conditions. This condition isn’t momentary and continues for a nontrivial amount of time. States are distinguishable if they differ in:

  • The actions performed within the state
  • The reachability graph of subsequent states
  • The messages (events) accepted while in the state and the transitions taken in response to them

    For example, a switch can be on or off. These are clearly conditions of existence that can last indefinitely and are therefore states. The weather in Oregon can be described either as raining or going to rain. A sensor can be off, calibrating, measuring, ready (idle without valid value), holding (idle with a valid value), or in error.

    An object can transition from one state to another in response to an event. An event is an occurrence of some significance that appears at an instant of time and does not persist. Events can be:

  • Conditions coming true
  • Elapsing of fixed-time intervals
  • Receipt of signals representing external occurrences

    An object stays in the same state until an event occurs which causes a transition to a new state.

    An action is essentially a function call in the programmatic sense. It is presumed to take an insignificant amount of time to execute. In statecharts, actions can occur in three different places: upon entry to a state, upon exit from a state, and when a transition is taken. Mathematically speaking, statecharts are no more expressive than Mealy-Moore state models (M&Ms), but are certainly more convenient and parsimonious.

    The syntax for transitions in UML is

    event-signature [guard] ‘/’ action-list ‘^’
    send-clause

    The fields are explained in Table 2. For example, the following are valid transitions:

    e1
    e2 / f(x)
    e3 ^ e4
    e5(x) [x < 0] / f(x), g(y) ^ e1, e3

    TABLE 2: Transition signature fields.

    Field
    Description
    Example

    event-signature
    Name of the event with an optional, comma-seperated parameter list enclosed with parenthesis
    e1
    e1(p)
    e1(int p, float q)

    guard
    Boolean condition that determines whether or not the transition will be taken when the event occurs
    [a < 10]
    [IN(ReadyState)]

    action-list
    List of actions to be executed when the transition is taken
    f()
    x=y+z
    g(x.p), h(), j(x)

    send-clause
    List of events to be sent to other state machines separated by commas
    e1, e2(x)

    All these fields are optional. When a transition is unnamed, it means that the transition can be taken immediately upon entering the precondition state.

    State machines suffer from a malady called state explosion . Representing all states and transitions of complicated objects often requires the creation of almost unreadable diagrams. UML statecharts allow reactive behavior to be decomposed in a couple of ways. First, states may be nested. Nested states are called or-states because the object must be in exactly one or-state at any level of nesting. Nested states are shown by drawing substates within an enclosing superstate. At each level of nesting, it is semantically important to identify the default substate if a transition is drawn to or from the enclosing superstate rather than the most deeply nested substate.

    The other method of decomposing states is through the use of orthogonal components. Many times, an object will consist of different parts which are more or less independent. Each of these independent parts may be defined using a separate state machine. In statechart terms, the independent parts of the object are called orthogonal components, and the resulting substates are called and-states. The state machines are separated from each other by dashed lines. Orthogonal components do not necessarily imply concurrency, but they support concurrency when necessary.

    The UML defines some special transitions connectors:

    Default Connector -a small circle from which the transition begins identifies the default transition when the object is created or when a superstate is entered.

    Terminal Connector -a circumscribed T or a dot within a circle identifies the termination of a state machine. This necessarily implies the destruction of the object.

    History Connector -a circumscribed H represents history. This allows the last active substate within a superstate to be the default substate. For example, a microwave oven exits its cooking state when the door is opened, but you might want it to reenter the same substate when the door is closed so that you don’t have to reprogram it. The history connector allows you to specify this exact behavior.

    Conditional Connector -a circumscribed C or a small diamond represents a branch or decision made using guards. The transition to the conditional connector names the event to which multiple guards (with different target states) will be applied. Each possible branch must have a guard. It’s up to the designer to ensure that the conditions are mutually exclusive. If more than one guard evaluates to true, the UML specification states that one transition will be taken, but you cannot in principle know which one it is.

    Complex Connector -also known as the compound connector. It is shown as either a small circle or a short straight line, oriented either vertically or horizontally. Forks and joins are examples of complex connectors. Complex connectors show explicit thread synchronization in concurrent state machines.

    The important aspects of statecharts are shown in Figure 5. The dashed lines of state S3 delineate the orthogonal components Part_X, Part_Y, and Part_Z. When the object is in state S3 it must be in exactly one state of its orthogonal substate components (such as X1, Y2, and Z3).

    Figure 5

    Also note in Figure 5 the ways in which the orthogonal components may share information.

    The same event may affect multiple orthogonal components at the same time. This is called a broadcast event . For example, event T5 is acted on in both Part_X and Part_Z states and event T7 is acted upon in both Part_Y and Part_Z states.

    One event may result in the issuance of another event which can affect yet another orthogonal component. This is called a propagated event . In the example, the transition on event T7 issues an event T6 when taken. This can then be acted upon any component (such as Part_X) which accepts that event.

    A transition may use the current state of another as a guard, using the predefined IN( ) operator. In the example, the transition from Z1 to Z3 in Part_Z will only be taken when the T5 event occurs and Part_2 is in substate Y2.

    Variables may be used within guards. The transition labeled T1 will result in transitions to superstate S3 if the variable x is greater than zero and S2 in all other conditions.

    Large Scale Structuring

    As Booch has noted, the class is a necessary but insufficient condition for decomposing large systems. To support bigger systems, UML provides three mechanisms: packages, module diagrams, and deployment diagrams. Packages, shown as tabbed folders, group related classes together in a higher-level structure. The UML itself doesn’t say how classes ought to be related to be within the same package, but generally speaking, class generalization hierarchies should all fall within a single package. I’ve found it most helpful to use packages to represent domains (application subject matters), so that a system might have domains such as "anesthesia," "device I/O," "user interface," "OS interface," "alarm," and "error handling."

    Module diagrams show the module structure of the software so that the allocation of classes and objects to modules (or files) can be shown. Deployment diagrams (previously called Process diagrams in the Booch method) show the organization of the hardware components relevant to the software and their relationships. Software components can be allocated to nodes on the deployment diagram. This is extremely useful for showing the physical architecture of multiprocessor embedded systems. Figure 6 is a deployment diagram for a multiprocessor anesthesia system.

    Figure 6

    Though packages are normally used in conjunction with classes and objects, they can also be applied to items on other kinds of diagrams, like nodes on deployment diagrams. They are a general-purpose grouping construct.

    Design Patterns

    One of the most active areas of research and work in the OO arena is in design patterns. A design pattern is a kind of template for a solution to a common type of problem. A design pattern consists of three elements:

  • A common problem
  • The problem context
  • A general solution

    For example, many real-time systems have safety requirements and must continue to be safe in the event of single-point faults. Note that I use the term "fault" here and not the term "failure." Faults are noncompliancy of an application. Failures-a subclass of faults-are noncompliance due to some breakdown of the system. Other faults may be systematic. These faults are called errors and are the only faults that software is capable of inflicting on the unsuspecting user. Real-time systems provide plenty of opportunities for both kinds of faults and as developers of safety-critical systems, we very much want to avoid both. 5 Since that is impossible, we must ensure that our systems remain safe even in the presence of faults.

    A general solution to this problem is the Diverse Redundancy Pattern (also known as the Homogeneous Redun-dancy Pattern). 6 This pattern improves safety by performing computation (including closed-loop actuation) using two parallel but differently implemented channels. That way, when one channel errs in some way, the other channel can take over. This is especially important in applications that have no fail-safe state, such as in-flight avionics systems that cannot simply shut down when things go bad. This general solution is a design pattern-a kind of design template that can be applied in a wide variety of similar problems.

    The UML shows design patterns using class diagrams with the addition of an oval denoting the pattern. Dependencies from the oval to the classes identify the roles the classes play within the design pattern. The Diverse Redundancy architectural pattern shown in Figure 7 is an example of a design pattern.

    Figure 7

    Most patterns are mechanistic in nature; that is, they’re limited to a small number of participating classes within a small context. The Diverse Redundancy Pattern is an architectural pattern because its use has a broad, sweeping impact on the system architecture.

    Extensibility Mechanisms

    As big as the UML is, it nevertheless adopts a minimalist perspective with the view that undermodeling is preferable to overmodeling and that whenever possible, user preferences should be accommodated. For example, UML doesn’t define the language in which constraints are written. True, the UML metamodel (written itself in UML) uses the Object Constraint Language (OCL) to express constraints; however, it does not force this syntax on the end user. The UML tries to provide the complete and rich set of modeling constructs and notations, but allows the end user to customize and extend the UML as well.7 The primary mechanisms for this are constraints, tagged values, and stereotypes.

    Constraints

    Constraints are restrictions or rules applied to various elements of a model. These can be preconditions (things that must be true prior to a service request), structural conditions ("this link refers to the same object"), postconditions (things that are guaranteed to be true following a service), timing ("the elapsed time between message A and B must be less than 350ms"), and so on. Constraints allow you to use general modeling constructs, such as a class or an association, and futher specify how it will be used in your system. For example, consider a doubly-linked structure. The main class (linkNode) has two links-one to the next node in the list and one to the previous. The associations, by definition, must be assigned in that way, but how should this information be captured?

    Constraints are enclosed within curly braces to make them more visually identifiable. For example, the constraints on the Prev and Next associations of this doubly-linked list might be specified as:

    { If Prev != NULL then Prev->Next
    == this;
    If Next != NULL then Next->Prev
    == this;
    }

    Timing specifications are another constraint of particular interest to real-time systems developers. Timing constraints are most often added to message sequence diagrams. These constraints can also be applied to operations of classes within class diagrams and in statecharts.

    The UML defines an OCL which it uses within its own definition, and developers are free to use this language in order to specify the constraints of their own system. However, developers should feel equally free to use whatever language they like for constraint specifications, including mathematical expressions, psuedocode, or even C++.

    Tagged Values

    Tagged values are user-assigned values associated with a modeling element. Most commonly, tagged values are used to bind other portions of the software development process more tightly with the analysis and design models. Tagged values may be applied to any modeling element and may reference formal requirements to be met by a class or operation, schedule dates on which the element is to be delivered or tested, links to a test plan (when, where, who), or to the individual developers or teams tasked with the element’s implementation. Although most of us have been doing this for years (or in some cases, decades), it’s nice to work with a modeling language that explicitly allows and encourages the construction of such links to other aspects of the development process.

    Stereotypes

    Stereotypes are specialization extensions of the UML metamodel itself. A stereotype is the type of the modeling element within the UML metamodel. This means that the user can take a metamodel element, such as the modeling elements class and package, and subclass them. Notationally, stereotype may be indicated either by associating a stereotype tag with the modeling element or by creating a specialized icon to represent the stereotype. Stereotype tags are labels enclosed by guillemets or double angled brackets, such as < > (applied to a class on a class diagram) or < > (applied to a node on a deployment diagram).

    The UML predefines a number of stereotypes and as a user, you should feel free to add more whenever it clarifies or simplifies your analysis and design models.

    Developing an Embedded System The UML Way

    The UML doesn’t detail any process, as it was decided that the modeling language should support any and all reasonable development processes. Rational does provide the Rational Objectory process, adapted from Ivar Jacobson’s previous work, but other development processes may be used. In general, most modelers agree that software development proceeds in phases consisting of analysis, design, coding, and testing. The order and sequence of these phases differs somewhat between process methods as well as the details of what constitutes each of these phases. A common modeling process will use UML in the way depicted in Table 3.

    TABLE 3: A common modeling process using UML.

    Primary Phase
    Subphase
    Activities

    Analysis
    Requirements Analysis
    Take concept statement and define detailed requirements sufficiently robust to define all externally visi- ble characteristics of the system. Also perform hazard analysis (for safety-relevant systems) and write a validation test plan that maps to the requirements specification

    .

    System Context Analysis
    Define the context of the system using use cases and scenarios. Externally visible messages, events, and actions are defined. The system is treated as a black box. Use care and scenario models are the deliver- ables although context diagrams may be done using object diagrams. For real-time systems, you probably want to characterize the timing (such as period, jitter, minimum interarrival time) and synchroniza- tion (such as blocking, balking, timeout, synchrounous) aspects of the messages and responses.

    Model Analysis
    Open the black box and identify the classes, objects, and associations that are inherent in the problem, using class and object diagrams. Required reactive behavior is modeled using statecharts. Inter- object behavior is shown with se- quence or collaboration diagrams.

    Design
    Architectural Design
    Define the global architectural decisions that have broad ramifica- tions. Physical architecture of the system is modeled using deploy- ment diagrams, software component architecture is modeled using component diagrams, and concur- rency models are captured using class diagrams identifying the active objects. Architectural design pat- terns are used here as well.

    Mechanistic Design
    Define the collaborative beavior of small clusters of classes and objects. Mechanistic design patterns are used here extensively. Design deci- sions are added by including design- level classes such as smart pointers, containers, and iterators. Most of this information is captured on class and object diagrams. Sequence and collaboration diagrams capture spe- cific instances of collaborations and statecharts are elaborated to define the full behavioral space.

    Detailed Design
    Define the detailed behavior and structure of individual classes using activity diagrams and textual nota tions such as constraints.

    Coding
    Write code in the target language.

    Testing
    Unit Testing
    Test the internal structure and be havior of each class.

    Integration Testing
    Test the integration of various ag gregations of components. This can take place recursively at multiple levels of decompostion, depending on the size of the system.

    Validation Testing
    Test the delivered system against the requirements as defined in the validation test plan.

    In our sample problem, we’ll provide a complete, if simplified, problem statement and begin with the analysis of the system context. Then we will open up the black box and identify some key objects and classes and how they relate to each other before we move into the design phase. In design, we’ll identify a multiprocessing architecture and use both architectural and mechanistic design patterns to identify the "glue" objects we want to add to implement the system.

    Coming Up in Parts II and III

    The next two parts of this series will present a sample real-time application problem statement-an anesthesia patient ventilator. The problem statement will be analyzed using the UML tools I’ve discussed in this article. In Part II, use case and scenario analysis will be applied to identify the key use cases and system scenarios. Analysis will be elaborated using object identification strategies in order to come up with a class model of the system. Architectural design decisions will be introduced here as well, in terms of the multiprocessing, tasking, and communications architecture. Part III will continue the work of elaborating this analysis model by elaborating state behavior of various objects and developing an efficient implementation model using design patterns.

    Bruce Powel Douglass has almost 20 years experience designing safety-critical embedded applications in a variety of hard real-time environments. He is currently employed as the Chief Evangelist at i-Logix, a real-time OO and system design automation tool vendor. He was on the oversight committee involved in the definition of the UML. His latest book, Real-Time UML: Developing Efficient Objects for Embedded Systems (Addison-Wesley in Reading, MA) is now available. Reach him at bpd@ilogix.com.

    References

    1. Ivar Jacobson is coming out with a process book called The Objectory Software Development Process (Reading, MA: Addison-Wesley, Spring 1998). My book, Real-Time UML: Efficient Objects for Embedded Systems (Reading, MA: Addison-Wesley, 1997) also includes material on software development process for real-time systems.
    2. Compared with 80% to 90% of the desktop applications developers.
    3. CORBAWE stands for CORBA-Wanna-bE.
    4. Specifically, each orthogonal component of an object must be in exactly one state in its lowest level of nesting. Objects with independent aspects may be modeled as a set of orthogonal components, each of which has its own state machine.
    5. Such as the F-16 bug that caused the plane to turn upside down when it crossed the equator.
    6. For details of this pattern, see Douglass, Real-Time UML: Efficient Objects for Embedded Systems .
    7. The UML is itself composed of about 90 meta-classes and over 100 meta-associations.

    Bibliography

    1. Douglass, Bruce Powel. Doing Hard Time: Using Object Oriented Programming and Software Patterns in Real Time Applications . Reading, MA: Addison-Wesley-Longman, Spring, 1998.
    2. Douglass, Bruce Powel. Real-Time UML: Efficient Objects for Embedded Systems . Reading, MA: Addison-Wesley-Longman, 1997.
    3. Fowler, Martin and Kendall Scott. UML Distilled: Applying the Standard Object Modeling Language . Reading, MA: Addison-Wesley-Longman, 1997.
    4. UML Notation Guide Version 1.1 , September, 1997. Rational Corp, et. al. As submitted to the OMG.
    5. UML Semantics Version 1.1 , September, 1997. Rational Corp, et. al. As submitted to the OMG.
    6. UML Summary Version 1.1 , September, 1997. Rational Corp, et. al. As submitted to the OMG.

     

  • Advertisements