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

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

Appendix A. Source Code for the ICP Simulator

A.1 Overview

If you want to experiment with the code in this book, you will require an implementation of the ICP API we used for our hypothetical instrument control protocol. The following two sections list source code to simulate the ICP network in your server.

Section A.2 shows source code for an in-memory implementation. This implementation keeps a map of the network devices in memory and provides access to the state of devices via the ICP API shown in Section 10.3. The state of the simulated network is not written to disk in this implementation, so when your server shuts down all state changes are lost.

Section A.3 augments the implementation with a very simple persistence mechanism.

A.2 Transient Simulator Code

The non-persistent ICP simulator applies to the server implementation in Chapter 10. The simulator uses an STL map that maps device IDs to structures of type DeviceState. A DeviceState structure stores the type (thermometer or thermostat), model string, location, and nominal temperature for each device. For simplicity, we use the same DeviceState structure for both types of devices even though thermometers do not have a nominal temperature. (The nominal_temp field is unused for thermometers.) The four API calls to add and remove devices and to access attributes manipulate the map of devices held in the static variable dstate.

Because we do not have real devices that would store a real model string in read-only memory, we use the asset number to assign a model string to each device: odd asset numbers denote thermometers, and even asset numbers denote thermostats.

In a real climate control system, the actual temperature of a room varies around the selected nominal temperature. The vary_temp function simulates this variation by returning a temperature that deviates by as much as three degrees from the passed temperature.

To decide what temperature to return for a particular device, we use the actual_temp function. Given an iterator that indicates the device whose temperature is to be returned, the function locates all thermostats in the same room as the given device and calculates the average of their nominal temperatures. That average is passed to vary_temp to simulate temperature fluctuations. If the given device is a thermometer in a room that contains no thermostat, the actual temperature fluctuates around DFLT_TEMP.

869

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

#include

<string>

#include

<map>

#include

<algorithm>

#include

<stdlib.h>

#include

"icp.h"

//----------------------------------------------------------------

enum DeviceType { thermometer, thermostat };

struct DeviceState {

// State for a device

DeviceType

type;

const char *

model;

string

location;

short

nominal_temp; // For thermostats only

};

typedef map<unsigned long, DeviceState> StateMap;

//----------------------------------------------------------------

// Max len of string including NUL

const size_t MAXSTR = 32;

const short MIN_TEMP = 40;

// 40

F == 4.44

C

const short MAX_TEMP = 90;

// 90

F == 32.22

C

const short DFLT_TEMP = 68;

// 68

F == 20.00

C

static StateMap dstate;

// Map of known devices

//----------------------------------------------------------------

 

 

 

//ICP_online() simulates adding a new device to the network by

//adding it to the dstate map.

//

//For this simple simulation, devices with odd a sset numbers

//are thermometers and devices with even asset numbers

//are thermostats.

//

//Thermostats get an initial nominal temperature of DFLT_TEMP.

//The location string is intentionally left blank because it

//must be programmed by the controller after putting the device

//on-line (as should be the nominal temperature).

//

//If a device with the specified ID is already on-line, the

//return value is -1. A zero return value indicates success. extern "C"

int

ICP_online(unsigned long id)

{

//Look for id in state map.

StateMap::iterator pos = dstate.find(id);

if (pos !=

dstate.end())

// Already exists

return

-1;

// Fill in

state.

 

DeviceState ds;

ds.type = (id % 2) ? thermometer : thermostat; ds.model = (ds.type == thermometer)

? "Sens-A-Temp" : "Select-A-Temp"; ds.nominal_temp = DFLT_TEMP;

// Insert new device into map dstate[id] = ds;

return 0;

}

//----------------------------------------------------------------

//ICP_offline() simulates removing a device from the network by

//removing it from the dstate map. If the device isn't known, the

//return value is -1. A zero return value indicates success.

870

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

extern "C"

 

int

 

ICP_offline(unsigned long id)

 

{

 

// Look for id in state map

 

StateMap::iterator pos = dstate.find(id);

 

if (pos == dstate.end())

// No such device

return -1;

dstate.erase(id);

 

return 0;

 

}

 

//----------------------------------------------------------------

 

//vary_temp() simulates the variation in actual temperature

//around a thermostat. The function randomly varies the

//temperature as a percentage of calls as follows:

//

3

degrees too cold:

5%

//

//

3

degrees too hot:

5%

//2 degrees too cold: 10%

//

2

degrees too hot:

10%

//

1

degree too cold:

15%

//

1

degree too hot:

15%

//exact temperature: 40%

static short

vary_temp(short temp)

{

long r = lrand48() % 50; long delta;

if (r < 5) delta = 3;

else if (r < 15) delta = 2;

else if (r < 30) delta = 1;

else

delta = 0;

if (lrand48() % 2) delta = -delta; return temp + delta;

}

//----------------------------------------------------------------

//Function object. Locates a thermostat that is in the same room

//as the device at position pos.

class ThermostatInSameRoom { public:

ThermostatInSameRoom(

const StateMap::iterator & pos ) : m_pos(pos) {}

bool operator()(

pair<const unsigned long, DeviceState> & p ) const

{

return(

p.second.type == thermostat

&&p.second.location

==m_pos->second.location

871

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

);

}

private:

const StateMap::iterator & m_pos;

}; //----------------------------------------------------------------

//actual_temp() is a helper function to determine the actual

//temperature returned by a particular thermometer or thermostat.

//The pos argument indicates the device.

//

//The function locates all thermostats that are in the same room

//as the device denoted by pos and computes the average of all

//the thermostats' nominal temperatures. (If no thermostats are

//in the same room as the device, the function assumes that the

//average of the nominal temperatures is DFLT_TEMP.)

//

//The returned temperature varies from the average as

//determined by vary_temp().

static short

actual_temp(const StateMap::iterator & pos)

{

long sum = 0; long count = 0;

StateMap::iterator where = find_if(

dstate.begin(), dstate.end(), ThermostatInSameRoom(pos)

); while (where != dstate.end()) {

count++;

sum += where->second.nominal_temp; where = find_if(

++where, dstate.end(), ThermostatInSameRoom(pos)

);

}

return vary_temp(count == 0 ? DFLT_TEMP : sum / count);

}

//----------------------------------------------------------------

//ICP_get() returns an attribute value of the device with the

//given id. The attribute is named by the attr parameter. The

//value is copied into the buffer pointed to by the value

//pointer. The len parameter is the size of the passed buffer,

//so ICP_get() can avoid overrunning the buffer.

//

//By default, thermometers report a temperature that varies

//somewhat around DFLT_TEMP. However, if there is another

//thermostat in the same room as the thermometer, the

//thermometer reports a temperature that varies around that

//thermostat's temperature. For several thermostats that are in

//the same room, the thermometer reports a temperature that

//varies around the average nominal temperature of all the

//thermostats.

//

//Attempts to read from a non-existent device or to read a

//non-existent attribute return -1. A return value of zero

//indicates success. If the supplied buffer is too short to hold

872

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

//a value, ICP_get() silently truncates the value and

//returns success.

extern "C"

 

int

 

ICP_get(

id,

unsigned long

const char *

attr,

void *

value,

size_t

len)

{

 

// Look for id in state map StateMap::iterator pos = dstate.find(id);

if (pos ==

dstate.end())

// No such device

return

-1;

//Depending on the attribute, return the

//corresponding piece of state.

if (strcmp(attr, "model") == 0) {

strncpy((char *)value, pos->second.model, len); } else if (strcmp(attr, "location") == 0) {

strncpy((char *)value, pos->second.location.c_str(), len); } else if (strcmp(attr, "nominal_temp") == 0) {

if (pos->second.type != thermostat)

// Must be thermostat

return -1;

memcpy(

 

value, &pos->second.nominal_temp, min(len, sizeof(pos->second.nominal_temp))

);

}else if (strcmp(attr, "temperature") == 0) { short temp = actual_temp(pos);

memcpy(value, &temp, min(len, sizeof(temp)));

}else if (strcmp(attr, "MIN_TEMP") == 0) { memcpy(value, &MIN_TEMP, min(len, sizeof(MIN_TEMP)));

}else if (strcmp(attr, "MAX_TEMP") == 0) { memcpy(value, &MAX_TEMP, min(len, sizeof(MAX_TEMP)));

}else {

return -1;

// No such attribute

}

// OK

return 0;

}

 

//----------------------------------------------------------------

 

//ICP_set() sets the attribute specified by attr to the

//value specified by value for the device with ID id. Attempts to

//write a string longer than MAXSTR bytes (including the

//terminating NUL) result in silent truncation of the string.

//Attempts to access a non-existent device or attribute

//return -1. Attempts to set a nominal temperature outside the

//legal range also return -1. A zero return value

//indicates success.

extern "C" int

ICP_set(unsigned long id, const char * attr, const void * value)

{

// Look for id in state map StateMap::iterator pos = dstate.find(id); if (pos == dstate.end())

return -1;

//

No such device

// Change either location or nominal temp,

depending on attr.

873

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

if (strcmp(attr, "location") == 0) { pos->second.location.assign(

(const char *)value, MAXSTR - 1

);

} else if (strcmp(attr, "nominal_temp") == 0)

{

 

if (pos->second.type != thermostat)

// Must be thermostat

return -1;

short temp;

 

memcpy(&temp, value, sizeof(temp));

if (temp < MIN_TEMP || temp > MAX_TEMP)

return -1;

 

pos->second.nominal_temp = temp;

 

} else {

// No such attribute

return -1;

}

// OK

return 0;

}

A.3 Persistent Simulator Code

The persistent simulator applies to the server implementations discussed in Chapter 12 and later chapters. This version of the simulator stores the state of the ICP network in the text file /tmp/CCS_DB, so the server can shut down and start up again without losing previous changes made to the network. The text file contains multiline records using one line for each device attribute:

Asset number

Device type (zero indicates a thermometer, 1 indicates a thermostat) Location

Nominal temperature (for thermometers only)

Here is a small example file containing a thermometer record followed by a thermostat record:

1027

0 ENIAC 3032 1

Colossus 68

To keep the /tmp/CCS_DB file up-to-date, we use a global class instance mydb. At start-up, the constructor of mydb reads the contents of the file and initializes the dstate map; at shutdown, the destructor writes the entire map contents back to the file. This design is not terribly elegant, but it has the advantage that the existence of the ICP simulator is hidden from the rest of the source code.

874

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

For simplicity, we keep error checking to a minimum. Also note that state changes are written out only if the server terminates cleanly. If the server terminates abnormally—for example, with a core dump or by calling _exit—the destructor of mydb never runs and all state changes are lost.

To add persistence to the implementation in Section A.2, we append the following code:

#include <fstream.h> class ICP_Persist { public:

ICP_Persist(const char * file); ~ICP_Persist();

private:

string m_filename;

};

// Read device state from a file and initialize the dstate map. ICP_Persist::

ICP_Persist(const char * file) : m_filename(file)

{

// Open input file, creating it if necessary.

fstream db(m_filename.c_str(), ios::in|ios::out, 0666); if (!db) {

cerr < "Error opening " < m_filename < endl; exit(1);

}

// Read device details, one attribute per line. DeviceState ds;

unsigned long id; while (db >> id) {

// Read device type and set model string accordingly. int dtype;

db >> dtype;

ds.type = dtype == thermometer

? thermometer : thermostat; ds.model = dtype == thermometer

? "Sens-A-Temp" : "Select-A-Temp";

char loc[MAXSTR];

// Skip newline

db.get(loc[0]);

db.getline(loc, sizeof(loc));

// Read location

ds.location = loc;

 

if (ds.type == thermostat)

// Read temperature

db >> ds.nominal_temp;

dstate[id] = ds;

// Add entry to map

}

 

db.close();

 

if (!db) {

 

cerr < "Error closing " < m_filename < endl; exit(1);

}

}

// Write device state to the file. ICP_Persist::

~ICP_Persist()

{

// Open input file, truncating it.

875

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

ofstream db(m_filename.c_str()); if (!db) {

cerr < "Error opening " < m_filename < endl; exit(1);

}

// Write the state details for each device. StateMap::iterator i;

for (i = dstate.begin(); i != dstate.end(); i++) { db < i->first < endl;

db < i->second.type < endl;

db < i->second.location < endl; if (i->second.type == thermostat)

db < i->second.nominal_temp < endl;

}

if (!db) {

cerr < "Error writing " < m_filename < endl; exit(1);

}

db.close(); if (!db) {

cerr < "Error closing " < m_filename < endl; exit(1);

}

}

// Instantiate a single global instance of the class. static ICP_Persist mydb("/tmp/CCS_DB");

876