Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
57
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

Chapter 10. Developing a Server for the Climate Control System

10.1 Chapter Overview

This chapter presents the source code for a complete climate control system server.

Section 10.2 introduces the overall implementation strategy, Section 10.3 presents the API for the instrument control protocol, and Sections 10.4 to 10.10 show the design and implementation of the classes and the main function used in the server. The complete source code for the server is listed in Section 10.11.

10.2 Introduction

Chapter 9 presents the C++ mapping for the server side, so we are now ready to look at a complete server implementation for the climate control system (CCS). (Before reading on, you may want to review the CCS IDL in Chapter 5.)

For this implementation, we use a simple strategy: the server maintains exactly one instantiated servant for each CORBA object in the system. In other words, the server contains a single servant for the controller singleton, and one servant for each device. In addition, all objects in the server are transient; if the server shuts down, the server forgets all state changes, and object references held by clients become non-functional. The set of thermometers and thermostats in the server is fixed, and clients cannot add devices to the system or remove them. For now, this simple strategy will be sufficient. (Chapters 11 and 12 show more sophisticated implementations that are persistent and offer life cycle operations.)

Throughout this chapter, we incrementally present the source code for the various server components as we discuss them. You can find the full code listing in Section 10.11 at the end of this chapter.

10.3 The Instrument Control Protocol API

To manipulate thermometers and thermostats from the server, we need an API that provides access to our proprietary instrument control protocol. To keep things simple, we use a minimal and hypothetical API known as the Instrument Control Protocol (ICP) API. ( Section A.2 shows an implementation of this API that you can use to simulate a network if you want to experiment with the source code in this book.) The API consists of four C functions defined in the header file icp.h:[1]

[1] If you believe that this API is unrealistically primitive, we beg to differ. We have seen reallife APIs for instrument control that are much worse.

#ifndef _ICP_H

347

IT-SC book: Advanced CORBA® Programming with C++

#define _ICP_H

 

 

extern "C" { {"

 

// Add device

int ICP_online(unsigned long id);

int ICP_offline(unsigned long id);

// Remove device

int ICP_get(

id,

// Get attribute

unsigned long

 

const char *

attr,

 

void *

value,

 

size_t

len

 

);

 

// Set attribute

int ICP_set(

id,

unsigned long

 

const char *

attr,

 

const void *

value

 

);

 

 

}

 

 

#endif /* _ICP_H */

The ICP functions use an unsigned long value as a network address. The network address of each device must be unique and corresponds to the asset number for thermometers and thermostats. All four functions in the ICP API return zero on success and -1 on failure.

The ICP network views each device as a collection of attributes. A device's attributes correspond directly to its hardware state, such as its register contents. Depending on the device type (thermometer or thermostat), an attribute may be read-only or writable. Any attribute value can be read, but only writable attribute values can be changed. For the climate control system, thermometers and thermostats have the attributes shown in

Table 10.1.

Note that the MIN_TEMP, MAX_TEMP, and nominal_temp attributes are supported only by thermostats. The MIN_TEMP and MAX_TEMP attributes provide the lowest and highest permissible setting of the corresponding thermostat, and nominal_temp contains the current setting of the thermostat. Attempts to read or write one of these attributes on a thermometer are rejected by the thermometer's hardware. Similarly, attempts to write to a read-only attribute are also rejected by the hardware.

For string-valued attributes, each device has a fixed 32-byte block of memory to hold the string (including a terminating NUL byte). Writing a longer string value for the location attribute results in silent truncation.

Table 10.1.. ICP thermometer attributes.

Attribute Name

Value Type

Size in Bytes

Mode

model

string

= 32

read-only

location

string

= 32

writable

temperature

short

2

read-only

MIN_TEMP[a]

short

2

read-only

348

IT-SC book: Advanced CORBA® Programming with C++

MAX_TEMP[a]

short

2

read-only

nominal_temp[a]

short

2

writable

[a]Supported by thermostats only.

10.3.1Adding and Removing Devices

int ICP_online(unsigned long id); int ICP_offline(unsigned long id);

We assume that our network does not support hardware discovery. Instead, the network must be informed of the existence of a newly connected device by a call to ICP_online that specifies the ID of the new device in the id parameter. ICP_online fails if the passed ID is already in use by another device.

Similarly, the network must be informed of physical disconnection of devices with a call to ICP_offline. The function fails if the passed id does not belong to a device that is currently connected to the network.

A more realistic instrument control protocol would be able to discover new devices automatically. We have chosen not to do this because it makes it easier to simulate the network in software (see Section A.2).

10.3.2 Reading Attribute Values

The ICP_get function reads the value of an attribute from the device specified by the id parameter.

int ICP_get(

id,

unsigned long

const char *

attr,

void *

value,

size_t

len

);

 

The name of the attribute to be read must be supplied as a string in the attr parameter. The function copies the attribute value into the value buffer, whose length must be provided in the len parameter.

ICP_get uses len to avoid overrunning the value buffer. If the value of an attribute does not fit into the value buffer, the value is silently truncated. For numeric attributes, ICP_get copies two bytes into value (provided len is at least 2). For string-valued attributes, ICP_get copies the NUL-terminated string value of the attribute. If a stringvalued attribute is truncated to len bytes, the truncated string is still NUL-terminated.

349

IT-SC book: Advanced CORBA® Programming with C++

The function fails and returns -1 if attempts are made to read from a device that is not on-line or if attr names a non-existent attribute.

Here is a C code fragment that reads the nominal temperature of device 686:

short temp;

if (ICP_get(686, "nominal_temp", &temp, sizeof(temp)) != 0) { /* No such device or attribute */

} else {

/* Got temperature */ printf("nominal_temp: %d\n", temp);

}

10.3.3 Writing Attribute Values

The ICP_set function updates the value of the attribute attr in the device specified by the id parameter.

int ICP_set(

id,

unsigned long

const char *

attr,

const void *

value

);

 

The value of the attribute is copied from the value buffer. For string-valued attributes, the string stored in the value buffer must be NUL-terminated. If a string is longer than 32 bytes (including the terminating NUL), it is silently truncated to fit. The function fails if attempts are made to set an attribute in a device that is not on-line or attempts are made to update a non-existent or read-only attribute.

Here is a C code fragment that updates the value of the location attribute of device 686:

const char buf[] = "Nearside Kitchen"; if (ICP_set(686, "location", buf) != 0) {

/* No such device or attribute, or read-only attribute */ } else {

/* Update was successful */

}

10.4 Designing the Thermometer Servant Class

The basic shape of thermometer servants is determined by the skeleton class produced by the IDL compiler. The thermometer servant must at least provide implementations for the four attributes in the Thermometer interface, so the basic class header looks like this:

350

model()
throw(CORBA::SystemException); asset_num()
throw(CORBA::SystemException);
temperature()
throw(CORBA::SystemException);
location()
throw(CORBA::SystemException); location(const char * loc)
throw(CORBA::SystemException);

IT-SC book: Advanced CORBA® Programming with C++

class Thermometer_impl : public virtual POA_CCS::Thermometer { public:

// CORBA attributes virtual CCS::ModelType

virtual CCS::AssetType

virtual CCS::TempType

virtual CCS::LocType

virtual void

};

Although we could leave this class as shown, we require a few more features for convenient use in our server.

The basic strategy for this implementation is to have one servant instantiated in memory for each device on the network. Each servant keeps its own asset number in a member variable called m_anum. The asset number (which is also an ICP network address) serves as the identity of each device. As you will see in Section 10.6, we use implementation inheritance to implement thermostats; the Thermostat_impl servant class inherits from the Thermometer_impl class in order to reuse its implementation. To allow the derived Thermostat_impl class to access its own identity (provided by the base class), we make m_anum a protected member, and, because the identity of a device is immutable for its lifetime, the m_anum is a const member.

The ICP API is not exactly a model of convenience. This suggests that we add private helper functions to the Thermometer_impl class to hide the details of accessing device attributes via the ICP API. We therefore add the helper functions get_model, get_temp, get_loc, and set_loc to the class.

Our object model contains the controller as a singleton object. As you will see in Section 10.5.3f, it is useful if each servant can access its controller object. Rather than make the controller a global variable, we add to the class a public data member called m_ctrl of type Controller_impl *, which points at the controller servant singleton. Because the member is static, it is shared by all thermometer and thermostat servants.

Our class will need a constructor and a destructor. For each instantiated device, the server must specify at least the asset number of the device. For this implementation, the constructor also accepts a location string. This is necessary because for now, our simple server will have a fixed number of devices at predetermined locations. (We discuss in Chapter 12 how clients can dynamically add and remove devices.)

351