00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 #include "power_state_estimator.h"
00036 
00037 #include <errno.h>
00038 #include <stdlib.h>
00039 #include <fstream>
00040 #include <iostream>
00041 #include <sys/file.h>
00042 #include <sys/stat.h>
00043 #include <sys/types.h>
00044 
00045 #include "ros/ros.h"
00046 
00047 using namespace std;
00048 using namespace power_monitor;
00049 
00050 
00051 
00052 PowerStateEstimator::PowerStateEstimator() { }
00053 
00054 void PowerStateEstimator::recordObservation(const PowerObservation& obs)
00055 {
00056     obs_ = obs;
00057 }
00058 
00059 bool PowerStateEstimator::canEstimate(const ros::Time& t) const
00060 {
00061     return t <= ros::Time::now() && obs_.getBatteries().size() > 0;
00062 }
00063 
00064 
00065 
00066 string                    FuelGaugePowerStateEstimator::getMethodName() const { return "fuel gauge"; }
00067 PowerStateEstimator::Type FuelGaugePowerStateEstimator::getMethodType() const { return FuelGauge;    }
00068 
00069 PowerStateEstimate FuelGaugePowerStateEstimator::estimate(const ros::Time& t)
00070 {
00071     PowerStateEstimate ps;
00072     ps.time_remaining    = obs_.getAcCount() > 0 ? obs_.getMaxTimeToFull(t) : obs_.getMinTimeToEmpty(t);
00073     ps.relative_capacity = obs_.getMinRelativeStateOfCharge();
00074 
00075     return ps;
00076 }
00077 
00078 
00079 
00080 const std::string AdvancedPowerStateEstimator::DEFAULT_LOG_FILE = "/var/ros/power_monitor/power.log";
00081 
00082 AdvancedPowerStateEstimator::AdvancedPowerStateEstimator()
00083 {
00084     ros::NodeHandle node("~");
00085 
00086     log_filename_ = DEFAULT_LOG_FILE;
00087     node.getParam("advanced_log_file", log_filename_);
00088     ROS_INFO("Using log file: %s", log_filename_.c_str());
00089 
00090     readObservations(log_);
00091     ROS_INFO("Read %d observations", (int) log_.size());
00092 }
00093 
00094 string                    AdvancedPowerStateEstimator::getMethodName() const { return "advanced"; }
00095 PowerStateEstimator::Type AdvancedPowerStateEstimator::getMethodType() const { return Advanced;   }
00096 
00097 void AdvancedPowerStateEstimator::recordObservation(const PowerObservation& obs)
00098 {
00099     PowerStateEstimator::recordObservation(obs);
00100 
00101     
00102     if (obs.getBatteries().size() != 16)
00103         return;
00104 
00105     LogRecord record;
00106     record.sec                          = obs.getStamp().sec;
00107     record.master_state                 = obs.getMasterState();
00108     record.charging                     = obs.getAcCount();
00109     record.total_power                  = obs.getTotalPower();
00110     record.min_voltage                  = obs.getMinVoltage();
00111     record.min_relative_state_of_charge = obs.getMinRelativeStateOfCharge();
00112     record.total_remaining_capacity     = obs.getTotalRemainingCapacity();
00113     log_.push_back(record);
00114 
00115     saveObservation(obs);
00116 }
00117 
00122 bool AdvancedPowerStateEstimator::hasEverDischarged() const
00123 {
00124     for (vector<LogRecord>::const_iterator i = log_.begin(); i != log_.end(); i++)
00125         if ((*i).master_state == pr2_msgs::PowerBoardState::MASTER_SHUTDOWN)
00126             return true;
00127 
00128     return false;
00129 }
00130 
00131 PowerStateEstimate AdvancedPowerStateEstimator::estimate(const ros::Time& t)
00132 {
00133     PowerStateEstimate ps;
00134 
00135     
00136     if (log_.size() > 0 && obs_.getAcCount() == 0 && hasEverDischarged())
00137     {
00138         
00139         unsigned int min_rsc     = 100;
00140         float        min_rem_cap = 999999.9;
00141         for (vector<LogRecord>::const_iterator i = log_.begin(); i != log_.end(); i++)
00142         {
00143             if ((*i).master_state == pr2_msgs::PowerBoardState::MASTER_SHUTDOWN)
00144             {
00145                 min_rsc     = min(min_rsc,     (*i).min_relative_state_of_charge);
00146                 min_rem_cap = min(min_rem_cap, (*i).total_remaining_capacity);
00147             }
00148         }
00149 
00150         
00151         float current        = obs_.getTotalPower() / obs_.getMinVoltage();
00152 
00153         float actual_rem_cap = obs_.getTotalRemainingCapacity() - min_rem_cap;
00154         float rem_hours      = actual_rem_cap / -current;
00155 
00156         ROS_DEBUG("current reported relative state of charge: %d", obs_.getMinRelativeStateOfCharge());
00157         ROS_DEBUG("minimum reported remaining capacity: %d", (int) min_rem_cap);
00158         ROS_DEBUG("minimum reported relative state of charge: %d", min_rsc);
00159         ROS_DEBUG("current: %.2f", current);
00160         ROS_DEBUG("report remaining capacity: %f", obs_.getTotalRemainingCapacity());
00161         ROS_DEBUG("time remaining: %.2f mins", rem_hours * 60);
00162 
00163         ps.time_remaining = ros::Duration(rem_hours * 60 * 60);
00164         ps.relative_capacity = int(100 * (obs_.getMinRelativeStateOfCharge() - min_rsc) / (100.0 - min_rsc));
00165     }
00166     else
00167     {
00168         
00169         ROS_DEBUG("No history (resorting to fuel gauge)");
00170         ROS_DEBUG("AC count: %d", obs_.getAcCount());
00171         ROS_DEBUG("current reported relative state of charge: %d", obs_.getMinRelativeStateOfCharge());
00172         ROS_DEBUG("maximum reported time-to-full: %d", obs_.getMaxTimeToFull(t).sec);
00173         ROS_DEBUG("minimum reported time-to-empty: %d", obs_.getMinTimeToEmpty(t).sec);
00174 
00175         ps.time_remaining = obs_.getAcCount() > 0 ? obs_.getMaxTimeToFull(t) : obs_.getMinTimeToEmpty(t);
00176         ps.relative_capacity = obs_.getMinRelativeStateOfCharge();
00177     }
00178 
00179     return ps;
00180 }
00181 
00182 void AdvancedPowerStateEstimator::tokenize(const string& str, vector<string>& tokens, const string& delimiters)
00183 {
00184     string::size_type last_pos = str.find_first_not_of(delimiters, 0);
00185     string::size_type pos      = str.find_first_of(delimiters, last_pos);
00186 
00187     while (string::npos != pos || string::npos != last_pos)
00188     {
00189         tokens.push_back(str.substr(last_pos, pos - last_pos));
00190 
00191         last_pos = str.find_first_not_of(delimiters, pos);
00192         pos      = str.find_first_of(delimiters, last_pos);
00193     }
00194 }
00195 
00196 bool AdvancedPowerStateEstimator::readObservations(vector<LogRecord>& log)
00197 {
00198     ifstream f(log_filename_.c_str(), ios::in);
00199     if (!f.is_open())
00200         return false;
00201 
00202     
00203     string line;
00204     getline(f, line);
00205     if (!f.good())
00206     {
00207         ROS_ERROR("Error reading header from log file: %s", log_filename_.c_str());
00208         return false;
00209     }
00210 
00211     int line_num = 0;
00212     while (true)
00213     {
00214         line_num++;
00215 
00216         getline(f, line);
00217         if (!f.good())
00218             break;
00219 
00220         vector<string> tokens;
00221         tokenize(line, tokens, ",");
00222 
00223         try
00224         {
00225             if (tokens.size() == 7)
00226             {
00227                 LogRecord record;
00228                 record.sec                          = boost::lexical_cast<uint32_t>(tokens[0]);
00229                 record.master_state                 = boost::lexical_cast<int>(tokens[1]);
00230                 record.charging                     = boost::lexical_cast<int>(tokens[2]);
00231                 record.total_power                  = boost::lexical_cast<float>(tokens[3]);
00232                 record.min_voltage                  = boost::lexical_cast<float>(tokens[4]);
00233                 record.min_relative_state_of_charge = boost::lexical_cast<unsigned int>(tokens[5]);
00234                 record.total_remaining_capacity     = boost::lexical_cast<float>(tokens[6]);
00235                 log.push_back(record);
00236                 continue;
00237             }
00238         }
00239         catch (const boost::bad_lexical_cast& ex) { }
00240 
00241         ROS_DEBUG("Invalid line %d in log file: %s.", line_num, log_filename_.c_str());
00242     }
00243 
00244     f.close();
00245 
00246     return true;
00247 }
00248 
00249 bool AdvancedPowerStateEstimator::saveObservation(const PowerObservation& obs) const
00250 {
00251     
00252     bool exists = false;
00253     FILE* f = fopen(log_filename_.c_str(), "r");
00254     if (f) {
00255         fclose(f);
00256         exists = true;
00257     }
00258 
00259     if (!exists)
00260     {
00261         
00262         size_t last_dir_sep = log_filename_.rfind("/");
00263         if (last_dir_sep == string::npos)
00264         {
00265             ROS_ERROR("Invalid log file: %s", log_filename_.c_str());
00266             return false;
00267         }
00268 
00269         string log_dir = log_filename_.substr(0, last_dir_sep);
00270 
00271         struct stat s;
00272         if (stat(log_dir.c_str(), &s) != 0)
00273         {
00274             
00275             ROS_INFO("Creating log file directory: %s", log_dir.c_str());
00276 
00277             mode_t old_umask = umask(0);
00278             int res = mkdir(log_dir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
00279             umask(old_umask);
00280             if (res != 0)      
00281             {
00282                 ROS_ERROR("Error creating power monitor log file directory");
00283                 return false;
00284             }
00285         }
00286     }
00287 
00288     
00289     mode_t old_umask = umask(0);
00290     int fd = open(log_filename_.c_str(), O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
00291     umask(old_umask);
00292     if (fd == -1)
00293     {
00294         ROS_ERROR("Error opening log file %s: %s", log_filename_.c_str(), strerror(errno));
00295         return false;
00296     }
00297     f = fdopen(fd, "a");
00298     if (f == NULL)
00299     {
00300         ROS_ERROR("Error opening log file for appending: %s", log_filename_.c_str());
00301         return false;
00302     }
00303 
00304     flock(fd, LOCK_SH);
00305     if (!exists)
00306         fprintf(f, "secs,master_state,charging,total_power,min_voltage,min_relative_state_of_charge,total_remaining_capacity\n");
00307     fprintf(f, "%d,%d,%d,%.3f,%.3f,%d,%.3f\n", obs.getStamp().sec, (int) obs.getMasterState(), obs.getAcCount(), obs.getTotalPower(), obs.getMinVoltage(), obs.getMinRelativeStateOfCharge(), obs.getTotalRemainingCapacity());
00308     fclose(f);
00309     close(fd);
00310 
00311     return true;
00312 }