// $Id$ // ============================================================================ // // = LIBRARY // TAO/examples/Advanced/ch_12 // // = FILENAME // icp.cpp // // = AUTHORS // Source code used in TAO has been modified and adapted from the // code provided in the book, "Advanced CORBA Programming with C++" // by Michi Henning and Steve Vinoski. Copyright // 1999. Addison-Wesley, Reading, MA. Used with permission of // Addison-Wesley. // // Modified for TAO by Mike Moran // // ============================================================================ #include #include #include #include #include "icp.h" using namespace std; // The following header is #included automatically by ACE+TAO. // Therefore, they don't need to be included explicitly. //#include 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 StateMap; const size_t MAXSTR = 32; // Max len of string including NUL 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 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 short vary_temp (short temp) { #if defined (__BORLANDC__) || defined (_MSC_VER) long r = rand() % 50; #else long r = lrand48() % 50; #endif long delta; if (r < 5) delta = 3; else if (r < 15) delta = 2; else if (r < 30) delta = 1; else delta = 0; #if defined (__BORLANDC__) || defined (_MSC_VER) if (rand() % 2) #else if (lrand48() % 2) #endif 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 short 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 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 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 (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) return -1; // Must be thermostat memcpy (value, &pos->second.nominal_temp, std::min (len, sizeof (pos->second.nominal_temp))); } else if (strcmp (attr, "temperature") == 0) { short temp = actual_temp (pos); memcpy (value, &temp, std::min (len, sizeof (temp))); } else if (strcmp (attr, "MIN_TEMP") == 0) { memcpy (value, &MIN_TEMP, std::min (len, sizeof (MIN_TEMP))); } else if (strcmp (attr, "MAX_TEMP") == 0) { memcpy (value, &MAX_TEMP, std::min (len, sizeof (MAX_TEMP))); } else { return -1; // No such attribute } return 0; // OK } // ICP_set () sets the the attributed 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 (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) return -1; // Must be thermostat short temp; 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 } #include 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. std::ifstream db (m_filename.c_str (), std::ios::in|std::ios::out);//, 0666); if (!db) { std::cerr << "Error opening " << m_filename << std::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]; db.get (loc[0]); // Skip newline db.getline (loc, sizeof (loc)); // Read location ds.location = loc; if (ds.type == thermostat) db >> ds.nominal_temp; // Read temperature 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 () { std::cout<<"~ICP_Persist"<first << std::endl; db << (unsigned long) (i->second.type) << std::endl; db << i->second.location << std::endl; if (i->second.type == thermostat) db << i->second.nominal_temp << std::endl; } if (!db) { std::cerr << "Error writing " << m_filename << std::endl; exit (1); } db.close (); if (!db) { std::cerr << "Error closing " << m_filename << std::endl; exit (1); } } // Instantiate a single global instance of the class. static ICP_Persist mydb ("/tmp/CCS_DB");