Introduction

Almost all computer systems in general, and embedded systems in particular, are event-driven, which means that they continuously wait for the occurrence of some external or internal event such as a time tick, an arrival of a data packet, a button press, or a mouse click. After recognizing the event, such systems react by performing the appropriate computation that may include manipulating the hardware or generating “soft” events that trigger other internal software components. (That's why event-driven systems are alternatively called reactive systems.) Once the event handling is complete, the software goes back to waiting for the next event.

You are undoubtedly accustomed to the basic sequential control, in which a program waits for events in various places in its execution path by either actively polling for events or passively blocking on a semaphore or other such operating system mechanism. Though this approach to programming event-driven systems is functional in many situations, it doesn't work very well when there are multiple possible sources of events whose arrival times and order you cannot predict and where it is important to handle the events in a timely manner. The problem is that while a sequential program is waiting for one kind of event, it is not doing any other work and is not responsive to other events.

Clearly, what we need is a program structure that can respond to a multitude of possible events, any of which can arrive at unpredictable times and in an unpredictable sequence. Though this problem is very common in embedded systems such as home appliances, cell phones, industrial controllers, medical devices and many others, it is also very common in modern desktop computers. Think about using a Web browser, a word processor, or a spreadsheet. Most of these programs have a modern graphical user interface (GUI), which is clearly capable of handling multiple events. All developers of modern GUI systems, and many embedded applications, have adopted a common program structure that elegantly solves the problem of dealing with many asynchronous events in a timely manner. This program structure is generally called event-driven programming.

Inversion of Control

Event-driven programming requires a distinctly different way of thinking than conventional sequential programs, such as “superloops” or tasks in a traditional RTOS. Most modern event-driven systems are structured according to the Hollywood principle, which means “Don't call us, we'll call you.” So an event-driven program is not in control while waiting for an event; in fact, it's not even active. Only once the event arrives, the program is called to process the event and then it quickly relinquishes the control again. This arrangement allows an event-driven system to wait for many events in parallel, so the system remains responsive to all events it needs to handle.

This scheme has three important consequences. First, it implies that an event-driven system is naturally divided into the application, which actually handles the events, and the supervisory event-driven infrastructure, which waits for events and dispatches them to the application. Second, the control resides in the event-driven infrastructure, so from the application standpoint the control is inverted compared to a traditional sequential program. And third, the event-driven application must return control after handling each event, so the execution context cannot be preserved in the stack-based variables and the program counter as it is in a sequential program. Instead, the event-driven application becomes a state machine, or actually a set of collaborating state machines that preserve the context from one event to the next in the static variables.

The Importance of the Event-Driven Framework

The inversion of control, so typical in all event-driven systems, gives the event-driven infrastructure all the defining characteristics of an application framework rather than a toolkit. When you use a toolkit, such as a traditional operating system or an RTOS, you write the main body of the application and call the toolkit code that you want to reuse. When you use a framework, you reuse the main body and write the code it calls.

Another important point is that an event-driven framework is actually necessary if you want to combine multiple event-driven state machines into systems. It really takes more than “just” an API, such as a traditional RTOS, to execute concurrent state machines. State machines require an infrastructure (framework) that provides, at a minimum, run-to-completion (RTC) execution context for each state machine, queuing of events, and event-based timing services. This is really the pivotal point. State machines cannot operate in a vacuum and are not really practical without an event-driven framework.

Active Object Computing Model

This book brings together two most effective techniques of decomposing event-driven systems: hierarchical state machines and an event-driven framework. The combination of these two elements is known as the active object computing model. The term active object comes from the UML and denotes an autonomous object engaging other active objects asynchronously via events. The UML further proposes the UML variant of statecharts with which to model the behavior of event-driven active objects.

In this book, active objects are implemented by means of the event-driven framework called QF, which is the main component of the QP event-driven platform. The QF framework orderly executes active objects and handles all the details of thread-safe event exchange and processing within active objects. QF guarantees the universally assumed RTC semantics of state machine execution, by queuing events and dispatching them sequentially (one at a time) to the internal state machines of active objects.

The fundamental concepts of hierarchical state machines combined with an event-driven framework are not new. In fact, they have been in widespread use for at least two decades. Virtually all commercially successful design automation tools on the market today are based on hierarchical state machines (statecharts) and incorporate internally a variant of an event-driven, real-time framework similar to QF.

The Code-Centric Approach

The approach I assume in this book is code-centric, minimalist, and low-level. This characterization is not pejorative; it simply means that you'll learn how to map hierarchical state machines and active objects directly to C or C++ source code, without big tools. The issue here is not a tool—the issue is understanding.

The modern design automation tools are truly powerful, but they are not for everyone. For many developers the tool simply can't pull its own weight and gets abandoned. For such developers, the code-centric approach presented in this book can provide a lightweight alternative to the heavyweight tools.

Most important, though, no tool can replace conceptual understanding. For example, determining which exit and entry actions fire in which sequence in a nontrivial state transition is not something you should discover by running a tool-supported animation of your state machine. The answer should come from your understanding of the underlying state machine implementation (discussed in Chapters 3 and 4). Even if you later decide to use a design automation tool and even if that particular tool would use a different statechart implementation technique than discussed in this book, you will still apply the concepts with greater confidence and more efficiency because of your understanding of the fundamental mechanisms at a low level.

In spite of many pressures from existing users, I persisted in keeping the QP event-driven platform lean by directly implementing only the essential elements of the bulky UML specification and supporting the niceties as design patterns. Keeping the core implementation small and simple has real benefits. Programmers can learn and deploy QP quickly without large investments in tools and training. They can easily adapt and customize the framework's source code to the particular situation, including to severely resource-constrained embedded systems. They can understand, and indeed regularly use, all the provided features.

Focus on Real-Life Problems

You can't just look at state machines and the event-driven framework as a collection of features, because some of the features will make no sense in isolation. You can only use these powerful concepts effectively if you are thinking about design, not simply coding. And to understand state machines that way, you must understand the problems with event-driven programming in general.

This book discusses event-driven programming problems, why they are problems, and how state machines and active object computing model can help. Thus, I begin most chapters with the programming problems the chapter will address. In this way, I hope to move you, a little at a time, to the point where hierarchical state machines and the event-driven framework become a much more natural way of solving the problems than the traditional approaches such as deeply nested IFs and ELSEs for coding stateful behavior or passing events via semaphores or event flags of a traditional RTOS.

Object Orientation

Even though I use C as the primary programming language, I also extensively use object-oriented design principles. Like virtually all application frameworks, QP uses the basic concepts of encapsulation (classes) and single inheritance as the primary mechanisms of customizing, specializing, and extending the framework to a particular application. Don't worry if these concepts are new to you, especially in C. At the C language level, encapsulation and inheritance become just simple coding idioms, which I introduce in Chapter 1. I specifically avoid polymorphism in the C version because implementing late binding in C is a little more involved. Of course, the C++ version uses classes and inheritance directly and QP/C++ applications can use polymorphism.

More Fun

When you start using the techniques described in this book, your problems will change. You will no longer struggle with 15 levels of convoluted ifelse statements, and you will stop worrying about semaphores or other such low-level RTOS mechanisms. Instead, you'll start thinking at a higher level of abstraction about state machines, events, and active objects. After you experience this quantum leap you will find, as I did, that programming can be much more fun. You will never want to go back to the “spaghetti” code or the raw RTOS.

How to Contact Me

If you have comments or questions about this book, the code, or event-driven programming in general, I'd be pleased to hear from you. Please e-mail me at [email protected].

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.48.131