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 }