Building Device Software Components


The .NET Micro Framework provides device drivers for many hardware components. These sit between your software and the physical interfaces that allow a program to interact with the outside world. We already discovered how to use some of these in Chapter 4. In this section, we will go into a little more detail and illustrate how you can create a message-based component and then use it in a situation where the program polls the data source.

An Embedded, Event-Driven Software Component

The basis of this work is an event-driven Global Positioning System (GPS) decoder. You could use it to make a flashlight that is location-aware and can record where and when it was last used.

The resources provided on the companion Web page for this book contain an implementation of this decoder. We designed it to be very extensible, in that you can use it for practically any protocol based on a sequence of text characters that deliver messages in a particular format. The GPS component accepts characters and performs the decoding of the message they contain.

 gps = new GPS(); gps.ReceiveChar('$'); 

The preceding code creates an instance of a GPS decoder and then delivers the character $ into it. The state machine within the decoder maintains the context of the message and will behave as appropriate. The character $ actually marks the start of a position message. When it has completed decoding such a message, the decoder generates an event to indicate that updated location information is available. Your program can bind to this event.

 gps.UpdateReceived += GPSUpdated; 

UpdateReceived is a member of the GPS class. It is a delegate type referring to a method that can accept a completion code.

 public delegate void GPSUpdate (int GPSMessageNumber); 

The user of a GPS instance can create a method of this type, which the GPS instance then calls when the program has successfully received an update.

 private static void GPSUpdated(int messageNumber) {      Debug.Print("GPS Update:"+ gps.LastMessageCode + " " + messageNumber); } 

This method just prints a diagnostic message; the production version would update the device display or otherwise use the new location information.

Because delegates support multiple endpoints, it is possible for multiple components to register an interest in position information provided by a single GPS instance.

Note 

If you create a single component that the program is to use in this way, you must bear in mind that there are issues of thread safety you must address. A thread that is reading location information from a GPS instance at the same time another thread is updating values may result in the presentation of invalid data. The present version of the component is not thread-safe in this respect, but it would not be difficult to add this behavior. Instead, the GPS component uses a message number to allow threads to determine when the coordinate information changes.

The GPS instance provides a method that allows the extraction of the location information it has obtained.

 int latDegrees, longDegrees; float latMinutes, longMinutes; char latDir, longDir; int messageNo; gps.GetPosition(    ref latDegrees, ref latMinutes, ref latDir,    ref longDegrees, ref longMinutes, ref longDir,    ref messageNo); 

The GetPosition method is provided with references to seven variables that are set with the different parts of the location information. Note that these must be references so that the method can store values in them.

GPS Component State and Properties

The state of the GPS component is managed in terms of an enumerated type which was created for this purpose:

 public enum GPSDecoderState {    waitingForStart,    readingCommand,    processingMessages,    readingChecksum,    gotResult } 

Variables of this type can hold only the given values, each of which represents a state occupied when decoding a message.

 $GPGLL,5347.0075,N,00025.1716,W,170319.782,A*27 

The preceding text shows a GPS position message. All messages start with a $ character, followed by the message name, a sequence of values, and finally a checksum. The initial state of the decoder is waitingForStart; when a $ character is received, the state is changed to readingCommand. Additional characters in the message will result in further state changes until the message has been completely decoded and validated.

 private GPSDecoderState stateValue; 

The member variable stateValue is held within the GPS component and used to manage the state of the message decoder. The variable is marked as private so that code in other classes cannot change the state of the decoder. However, we can make the value visible to other classes by providing a public property.

 public GPSDecoderState State {    get    {       return stateValue;    } } 

The property State in the preceding code returns the state of the GPS decoder, but it does not allow the state to be set, since only the get behavior has been specified. This technique is very useful if you want to provide external classes with the value of an item but do not wish to allow them to change that value.

Improving Flexibility by Using Components

The precise working of the decoder itself is beyond the scope of this text, but the full source code is available within the Logging Flashlight project in the example code for this chapter, which you can get from the companion Web page for this book. You can use this source code to decode GPS messages or as the basis of other serial protocol decoders. However, a number of program design issues are particularly relevant to embedded development at this point and are worth considering.

The component we have created is equally at home on a Windows personal computer running .NET and inside a Windows Mobile powered device. All these platforms use slightly different serial port configurations and display devices, but because the fundamental GPS decoding behavior they require is the same, we can integrate the component into projects on all such machines. It is even possible to make use of exactly the same library file in code across these platforms, which is an incredibly impressive demonstration of the flexibility of the .NET Architecture.

Note that the GPS class is also independent of the means by which the actual data arrives on the device that wants to decode raw GPS data. The character data to be decoded could come from a serial port, a log file, or a network port.

Improving Reusability by Using Components

The same component can be the basis of the behavior required for the decoding of any form of serial data in other situations. The pattern of delivering raw data messages to a device and receiving events when it has decoded something of interest is extremely useful in an embedded situation.

Improving Component Testability by Using Components

A GPS device generates a stream of data describing its position. To change the data produced by a device, you have to move it to a new location. However, it would not be practical to test the behavior of the decoding software by doing this.

If we want to test the GPS decoder with invalid data (for example, messages containing an incorrect checksum), we cannot use a genuine device at all. Instead we can make use of our ability to feed the GPS component a particular data stream and observe the result it produces. By devising test sequences by hand and logging messages from a GPS device, we can create data that can be fed into the decoder and used to verify its behavior.

Furthermore, we did not create the first version of the component on a .NET Micro Framework device at all, but instead wrote and tested it as part of a Windows application on a PC. Only when the component passed the tests on this platform did we then integrate it into the target environment for final testing.

We were even able to create a custom COM port emulator component that would pass through signals from a PC-based GPS device or send a preloaded file into the .NET Micro Framework code running inside the emulator. This allowed us to test the behavior of code running in the .NET Micro Framework with live or recorded position data.

You can find out more about creating emulator components in Chapter 9, "Developing with the Emulator."

This approach indicates a huge potential benefit to developers using the .NET Micro Framework. The .NET Micro Framework emulator platform makes testing applications in a simulation of the hardware easy, but you can write parts of an application in a Windows environment before moving them onto the target platform.

Improving Device Testability by Using Components

The GPS component that we have created will deliver location information to the device that is using it. As an example, a version of the flashlight program could contain a display that allows a security guard to see where he or she is on the site. We may want to test the behavior of the flashlight without walking around the site.

Because the GPS component delivers position information back to the system on the host device, it can provide test sequences of position information very easily. It is therefore easy to create a test GPS component that delivers recorded streams of position information.

Viewed in the context of a situation where teams are developing different components in parallel, this way of working is really useful. Once the specification of the GPS decoder behaviors has been agreed upon, the team creating the display component can use a simulated GPS signal to create and test their display behaviors. You can find out how to create the interfaces in the section "C# Interfaces" later in this chapter.

Note 

In the experience of the authors, the development of an appropriate environment for the use of a Test Driven Development methodology is one of the best ways to ensure that development is painless and ultimately successful. Starting to build a system, particularly one that includes hardware interaction, without considering how to properly test the code will usually doom a project to failure, either because you never get the product to work, or because the customer reports failures that you are unable to replicate and/or debug.

Creating a Message Pump

Now that we have a GPS component, we need to be able to feed it events. Many of the devices in the .NET Micro Framework can already generate events. We can bind the event's methods that run when the event occurs. We have seen that this is very useful, in that a program can just wait for the event to occur before dealing with it. However, some of the devices, for example, the serial port, do not work in this way. Instead, the program must poll them.

Note 

This is in contrast to the System.IO.Port.SerialPort class provided by the full .NET Framework, which provides a DataReceived event.

We have already seen how you can use the serial port to read individual characters in the .NET Micro Framework. We will now consider how we can create a message pump that will send those characters on to our GPS component.

 private static void monitorGPSData() {    byte[] buffer = new byte[1000];    while (true)    {       int count = com1port.Read(           buffer,                     // destination           0,                          // start position           buffer.Length,              // buffer size           100                         // wait 100 milliseconds       );       if (count > 0)       {          for (int i = 0; i < count; i++)          {             gps.ReceiveChar((char)buffer[i]);          }       }    } } 

The method monitorGPSData provides the message pump for our GPS device. The code executes it on a separate thread and makes use of the Read method on the COM port connected to the serial port. The method call waits 100 milliseconds before timing out. Then, it passes the received characters as events into the GPS instance.

The performance of the .NET Micro Framework device and the speed of the serial port in use determine the length of the byte buffer and the timeout for the Read call. In the case of the emulator, we have found that the figures used in the previous code give reasonable behavior on a standard desktop PC. Receipt of characters at the rate transmitted by serially interfaced GPS devices is well within the capability of NET Micro Framework devices.

At the moment, the GPS decoder class delivers the data characters individually. A possible improvement would be the addition of a method that allows a number of data bytes to be delivered in a single transaction.

C# Interfaces

The GPS decoder class exposes its behaviors in an interface implemented in the code. An interface is a C# construction similar to a class, but instead of providing methods containing behaviors, it has a list of method signatures describing the tasks that the class must be able to accomplish. As an example, the interface describing the GPS component is as follows:

 interface IGPS {    GPSDecoderState State    {       get;    }    GPSUpdate UpdateReceived    {       get;       set;    }    string LastMessageCode    {       get;    }    bool GetPosition(       ref int LatDegrees, ref float LatMinutes, ref char LatDir,       ref int LongDegrees, ref float LongMinutes, ref char LongDir,       ref int MessageNo);    void ReceiveChar(char ch);    void Reset(); } 

This interface encapsulates the behaviors that a GPS device must have to be a valid one. It must allow access to the UpdateReceived delegate so that clients can bind their own methods to this event. It must provide a ReceiveChar method that it can call to deliver characters to it for decoding, and it must provide methods that it can use to read the position information from the GPS information and the state of the decoder. It also has a Reset method that it can use to place the decoder into a known good state.

Some of these interface requirements take the form of properties. The LastMessageCode property provides a string containing the entire last successfully decoded message. Note the interface requires that the class provide a property that it can read (using Get) but does not require that the class allow us to set the last message code string. This is because we do not want external classes changing the value of the property, but we do want them to be able to look at the data. This illustrates a very powerful aspect of C# properties; they provide a means by which you can allow users of your classes read-only access to data.

There is a convention that interface names begin with the letter I, so the interface IGPS describes exactly what a class must be able to do in order that it may be thought of as a GPS decoder. Because the GPS class provides all these methods and has made them all public, we can tell C# that the class implements the IGPS interface.

 public class GPS : IGPS {    // must have all the methods and properties in IGPS here } 

Now, in the class that uses the GPS decoder, we can write this:

 IGPS myGPS = new GPS(); 

This line created a variable that can refer to classes that implement the IGPS interface and then made it refer to a new GPS instance.

By now, you might be wondering what the point of all this is. We seem to have done a lot of work and just created a new way to view our GPS class. This is true, but it is also the case that we created a new way we can view any object. If an object implements the IGPS interface, we can just plug it into any class that needs a GPS device. This has powerful implications for program design. We can very easily swap one GPS decoder for another one, just by making the new class implement the required interface and then using that instead. Previously, the consumer of the GPS device had to work with that particular class and no other. Now, the consumer of the GPS can work with any class that can behave as a GPS decoder. This could include test classes that return a predetermined set of coordinates or error conditions.

Interfaces allow you to regard a class in terms of its capabilities rather than restrict yourself to using a particular type in a given situation. A class can implement any number of interfaces, which means that a universal test device could behave as a GPS device and a data source as well.

Design with Components and Interfaces

Interfaces work best when included in the design of a system, both in terms of how the finished components work together and how you will test them. This is especially true when creating embedded systems, because extensive testing in the live device may be problematic, particularly when you need to test error conditions. You can isolate software into components that expose their behaviors in terms of the interfaces that they implement.

If producers and consumers of data always interact through such interfaces, it means that you can interchange software components without any need to change them internally. A test harness can enclose a component and both feed it data and view the results by incorporating the two interfaces used to express these behaviors. In the case of our GPS components, we have made a test GPS component that always delivers the content of a GPS log for a 25-mile road trip. We can plug it into any device that wishes to travel this route.




Embedded Programming with the Microsoft .Net Micro Framework
Embedded Programming with the Microsoft .NET Micro Framework
ISBN: 0735623651
EAN: 2147483647
Year: 2007
Pages: 118

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net