//============================================================================= /** * @file icp.cpp * * @author Source code used in TAO has been modified and adapted from the codeprovided in the book * @author "Advanced CORBA Programming with C++" by MichiHenning and Steve Vinoski. Copyright 1999. Addison-Wesley * @author Reading * @author MA.Modified for TAO by Mike Moran */ //============================================================================= #include #include #include //#include #include "icp.h" using namespace std; //---------------------------------------------------------------- enum DeviceType { thermometer, thermostat }; struct DeviceState { // State for a device DeviceType type; const char * model; string location; short nominal_temp; // For thermostats only }; typedef std::map StateMap; //---------------------------------------------------------------- const size_t MAXSTR = 32; // Max len of string including NUL const long MIN_TEMP = 40; // 40 F == 4.44 C const long MAX_TEMP = 90; // 90 F == 32.22 C const long 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 asset 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()) return -1; // Already exists // 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. extern "C" int ICP_offline(unsigned long id) { // Look for id in state map StateMap::iterator pos = dstate.find(id); if (pos == dstate.end()) return -1; // No such device 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 long vary_temp(long temp) { long r = ACE_OS::rand() % 50; long delta; if (r < 5) delta = 3; else if (r < 15) delta = 2; else if (r < 30) delta = 1; else delta = 0; if (ACE_OS::rand() % 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 & p ) const { return( p.second.type == thermostat && p.second.location == m_pos->second.location ); } 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 long actual_temp(const StateMap::iterator & pos) { long sum = 0; long count = 0; StateMap::iterator where = std::find_if( dstate.begin(), dstate.end(), ThermostatInSameRoom(pos) ); while (where != dstate.end()) { count++; sum += where->second.nominal_temp; where = std::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 // a value, ICP_get() silently truncates the value and // returns success. extern "C" int ICP_get( unsigned long id, const char * attr, void * value, size_t len) { // Look for id in state map StateMap::iterator pos = dstate.find(id); if (pos == dstate.end()) return -1; // No such device // Depending on the attribute, return the // corresponding piece of state. if (ACE_OS::strcmp(attr, "model") == 0) { ACE_OS::strncpy((char *)value, pos->second.model, len); } else if (ACE_OS::strcmp(attr, "location") == 0) { ACE_OS::strncpy((char *)value, pos->second.location.c_str(), len); } else if (ACE_OS::strcmp(attr, "nominal_temp") == 0) { if (pos->second.type != thermostat) return -1; // Must be thermostat ACE_OS::memcpy( value, &pos->second.nominal_temp, ace_min(len, sizeof(pos->second.nominal_temp)) ); } else if (ACE_OS::strcmp(attr, "temperature") == 0) { long temp = actual_temp(pos); ACE_OS::memcpy(value, &temp, ace_min(len, sizeof(temp))); } else if (ACE_OS::strcmp(attr, "MIN_TEMP") == 0) { ACE_OS::memcpy(value, &MIN_TEMP, ace_min(len, sizeof(MIN_TEMP))); } else if (ACE_OS::strcmp(attr, "MAX_TEMP") == 0) { ACE_OS::memcpy(value, &MAX_TEMP, ace_min(len, sizeof(MAX_TEMP))); } else { return -1; // No such attribute } return 0; // OK } //---------------------------------------------------------------- // 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. if (ACE_OS::strcmp(attr, "location") == 0) { pos->second.location.assign( (const char *)value, MAXSTR - 1 ); } else if (ACE_OS::strcmp(attr, "nominal_temp") == 0) { if (pos->second.type != thermostat) return -1; // Must be thermostat short temp; ACE_OS::memcpy(&temp, value, sizeof(temp)); if (temp < MIN_TEMP || temp > MAX_TEMP) return -1; pos->second.nominal_temp = temp; } else { return -1; // No such attribute } return 0; // OK }