power_node_simulator.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <stdio.h>
00031 #include <stdint.h>
00032 #include <string.h>
00033 #include <unistd.h>
00034 #include <stdlib.h>
00035 #include <sys/select.h>
00036 #include <sys/types.h>
00037 #include <assert.h>
00038 #include <errno.h>
00039 #include <signal.h>
00040 #include <vector>
00041 #include <sstream>
00042 #include <iostream>
00043 #include <boost/thread/thread.hpp>
00044 
00045 // Internet/Socket stuff
00046 #include <sys/types.h>
00047 #include <sys/socket.h>
00048 #include <netinet/in.h>
00049 #include <arpa/inet.h>
00050 #include <sys/socket.h>
00051 #include <net/if.h>
00052 #include <sys/ioctl.h>
00053 
00054 #include "power_comm.h"
00055 #include "power_node.h"
00056 #include "diagnostic_msgs/DiagnosticMessage.h"
00057 #include "diagnostic_updater/DiagnosticStatusWrapper.h"
00058 #include "rosconsole/macros_generated.h"
00059 #include "ros/console.h"
00060 
00061 using namespace std;
00062 
00063 // Keep a pointer to the last message recieved for
00064 // Each board.
00065 static std::vector<Device*> Devices;
00066 static PowerBoard *myBoard;
00067 
00068 void processSentMessage(CommandMessage *cmd);
00069 
00070 void Device::setTransitionMessage(const TransitionMessage &newtmsg)
00071 {
00072   if (tmsgset)
00073   {
00074 #define PRINT_IF_CHANGED(val)                                                                                 \
00075 for (int counter = 0; counter < 3; counter++)                                                                 \
00076 {                                                                                                             \
00077   if (tmsg.cb[counter].val##_count != newtmsg.cb[counter].val##_count)                                        \
00078   {                                                                                                           \
00079     ROS_INFO("Power board: CB%i "#val" event count changed to %i.", counter, newtmsg.cb[counter].val##_count);\
00080   }                                                                                                           \
00081 }
00082 
00083   PRINT_IF_CHANGED(stop);
00084   PRINT_IF_CHANGED(estop);
00085   PRINT_IF_CHANGED(trip);
00086   PRINT_IF_CHANGED(fail_18V);
00087   PRINT_IF_CHANGED(disable);
00088   PRINT_IF_CHANGED(start);
00089   PRINT_IF_CHANGED(pump_fail);
00090   PRINT_IF_CHANGED(reset);
00091 
00092 #undef PRINT_IF_CHANGED
00093   }
00094   else
00095     tmsgset = 1;
00096 
00097   tmsg = newtmsg;
00098 }
00099 
00100 void Device::setPowerMessage(const PowerMessage &newpmsg)
00101 {
00102   if (pmsgset)
00103   {
00104 #define PRINT_IF_CHANGED(val)                                                            \
00105 if (pmsg.status.val##_status != newpmsg.status.val##_status)                             \
00106 {                                                                                        \
00107   ROS_INFO("Power board: status of "#val" changed to %i.", newpmsg.status.val##_status); \
00108 } 
00109 
00110   PRINT_IF_CHANGED(CB0);  
00111   PRINT_IF_CHANGED(CB1);
00112   PRINT_IF_CHANGED(CB2);
00113   PRINT_IF_CHANGED(estop_button);
00114   PRINT_IF_CHANGED(estop);
00115 
00116 #undef PRINT_IF_CHANGED
00117   }
00118   else
00119     pmsgset = 1;
00120 
00121   pmsg = newpmsg;
00122 }
00123 
00124 int PowerBoard::send_command(unsigned int serial_number, int circuit_breaker, const std::string &command, unsigned flags)
00125 {
00126   if (Devices.size() == 0) {
00127     fprintf(stderr,"No devices to send command to\n");
00128     return -1;
00129   }
00130 
00131   int selected_device = -1;
00132   // Look for device serial number in list of devices...
00133   for (unsigned i = 0; i<Devices.size(); ++i) {
00134     if (Devices[i]->getPowerMessage().header.serial_num == serial_number) {
00135       selected_device = i;
00136       break;
00137     }
00138   }
00139 
00140   if ((selected_device < 0) || (selected_device >= (int)Devices.size())) {
00141     fprintf(stderr, "Device number must be between 0 and %u\n", Devices.size()-1);
00142     return -1;
00143   }
00144 
00145   Device* device = Devices[selected_device];
00146   assert(device != NULL);
00147 
00148 
00149   if ((circuit_breaker < 0) || (circuit_breaker > 2)) {
00150     fprintf(stderr, "Circuit breaker number must be between 0 and 2\n");
00151     return -1;
00152   }
00153 
00154   ROS_DEBUG("circuit=%d command=%s flags=%x\n", circuit_breaker, command.c_str(), flags);
00155 
00156   // Determine what command to send
00157   char command_enum = NONE;
00158   if (command == "start") {
00159     command_enum = COMMAND_START;
00160   }
00161   else if (command ==  "stop") {
00162     command_enum = COMMAND_STOP;
00163   }
00164   else if (command == "reset") {
00165     command_enum = COMMAND_RESET;
00166   }
00167   else if (command == "disable") {
00168     command_enum = COMMAND_DISABLE;
00169   }
00170   else if (command == "none") {
00171     command_enum = NONE;
00172   }
00173   /*else if (command == "terrible_hack_shutdown") {
00174     exit(0);
00175   }*/
00176   else {
00177     ROS_ERROR("invalid command '%s'", command.c_str());
00178     return -1;
00179   }
00180   //" -c : command to send to device : 'start', 'stop', 'reset', 'disable'\n"
00181 
00182 
00183   // Build command message
00184   CommandMessage cmdmsg;
00185   memset(&cmdmsg, 0, sizeof(cmdmsg));
00186   cmdmsg.header.message_revision = CURRENT_MESSAGE_REVISION;
00187   cmdmsg.header.message_id = MESSAGE_ID_COMMAND;
00188   cmdmsg.header.serial_num = device->getPowerMessage().header.serial_num;
00189   //cmdmsg.header.serial_num = 0x12345678;
00190   strncpy(cmdmsg.header.text, "power command message", sizeof(cmdmsg.header.text));
00191   cmdmsg.command.CB0_command = NONE;
00192   cmdmsg.command.CB1_command = NONE;
00193   cmdmsg.command.CB2_command = NONE;
00194   if (circuit_breaker==0) {
00195     cmdmsg.command.CB0_command = command_enum;
00196   }
00197   else if (circuit_breaker==1) {
00198     cmdmsg.command.CB1_command = command_enum;
00199   }
00200   else if (circuit_breaker==2) {
00201     cmdmsg.command.CB2_command = command_enum;
00202   }
00203   else if (circuit_breaker==-1) {
00204     cmdmsg.command.CB0_command = command_enum;
00205     cmdmsg.command.CB1_command = command_enum;
00206     cmdmsg.command.CB2_command = command_enum;
00207   }
00208 
00209   cmdmsg.command.flags = flags;
00210 
00211   errno = 0;
00212   
00213   processSentMessage(&cmdmsg);
00214 
00215   ROS_DEBUG("Send to Serial=%u, revision=%u", cmdmsg.header.serial_num, cmdmsg.header.message_revision);
00216   ROS_DEBUG("Sent command %s(%d) to device %d, circuit %d",
00217          command.c_str(), command_enum, selected_device, circuit_breaker);
00218 
00219   return 0;
00220 }
00221 
00222 
00223 const char* PowerBoard::cb_state_to_str(char state)
00224 {
00225   //enum CB_State { STATE_NOPOWER, STATE_STANDBY, STATE_PUMPING, STATE_ON, STATE_DISABLED };
00226   switch(state) {
00227   case STATE_NOPOWER:
00228     return "no-power";
00229   case STATE_STANDBY:
00230     return "Standby";
00231   case STATE_PUMPING:
00232     return "pumping";
00233   case STATE_ON:
00234     return "On";
00235   case STATE_DISABLED:
00236     return "Disabled";
00237   }
00238   return "???";
00239 }
00240 
00241 const char* PowerBoard::master_state_to_str(char state)
00242 {
00243   //enum CB_State { STATE_NOPOWER, STATE_STANDBY, STATE_PUMPING, STATE_ON, STATE_DISABLED };
00244   switch(state) {
00245   case MASTER_NOPOWER:
00246     return "no-power";
00247   case MASTER_STANDBY:
00248     return "stand-by";
00249   case MASTER_ON:
00250     return "on";
00251   case MASTER_OFF:
00252     return "off";
00253   }
00254   return "???";
00255 }
00256 
00257 
00258 // Determine if a record of the device already exists...
00259 // If it does use newest message a fill in pointer to old one .
00260 // If it does not.. use
00261 int PowerBoard::process_message(const PowerMessage *msg)
00262 {
00263   if (msg->header.message_revision != CURRENT_MESSAGE_REVISION) {
00264     ROS_WARN("Got message with incorrect message revision %u\n", msg->header.message_revision);
00265     return -1;
00266   }
00267 
00268   // Look for device serial number in list of devices...
00269   for (unsigned i = 0; i<Devices.size(); ++i) {
00270     if (Devices[i]->getPowerMessage().header.serial_num == msg->header.serial_num) {
00271       boost::mutex::scoped_lock(library_lock_);
00272       Devices[i]->message_time = ros::Time::now();
00273       Devices[i]->setPowerMessage(*msg);
00274       return 0;
00275     }
00276   }
00277 
00278   // Add new device to list
00279   Device *newDevice = new Device();
00280   Devices.push_back(newDevice);
00281   newDevice->message_time = ros::Time::now();
00282   newDevice->setPowerMessage(*msg);
00283   return 0;
00284 }
00285 
00286 int PowerBoard::process_transition_message(const TransitionMessage *msg)
00287 {
00288   if (msg->header.message_revision != CURRENT_MESSAGE_REVISION) {
00289     ROS_WARN("Got message with incorrect message revision %u\n", msg->header.message_revision);
00290     return -1;
00291   }
00292 
00293   // Look for device serial number in list of devices...
00294   for (unsigned i = 0; i<Devices.size(); ++i) {
00295     if (Devices[i]->getPowerMessage().header.serial_num == msg->header.serial_num) {
00296       boost::mutex::scoped_lock(library_lock_);
00297       Devices[i]->setTransitionMessage(*msg);
00298       return 0;
00299     }
00300   }
00301 
00302   // Add new device to list
00303   Device *newDevice = new Device();
00304   Devices.push_back(newDevice);
00305   newDevice->setTransitionMessage(*msg);
00306   return 0;
00307 }
00308 
00309 PowerBoard::PowerBoard( unsigned int serial_number ): ros::Node ("pr2_power_board")
00310 {
00311 
00312   ROSCONSOLE_AUTOINIT;
00313   log4cxx::LoggerPtr my_logger = log4cxx::Logger::getLogger(ROSCONSOLE_DEFAULT_NAME);
00314 
00315   if( my_logger->getLevel() == 0 )    //has anyone set our level??
00316   {
00317     // Set the ROS logger
00318     my_logger->setLevel(ros::console::g_level_lookup[ros::console::levels::Info]);
00319   }
00320 
00321   advertiseService("power_board_control", &PowerBoard::commandCallback);
00322   advertise<diagnostic_msgs::DiagnosticMessage>("/diagnostics", 2);
00323 }
00324 
00325 bool PowerBoard::commandCallback(pr2_power_board::PowerBoardCommand::Request &req_,
00326                      pr2_power_board::PowerBoardCommand::Response &res_)
00327 {
00328   res_.retval = send_command( req_.serial_number, req_.breaker_number, req_.command, req_.flags);
00329 
00330   return true;
00331 }
00332 
00333 void PowerBoard::sendDiagnostic()
00334 {
00335   ros::Rate r(1);
00336   while(node_handle.ok())
00337   {
00338     r.sleep();
00339     //ROS_DEBUG("-");
00340     boost::mutex::scoped_lock(library_lock_);
00341   
00342     for (unsigned i = 0; i<Devices.size(); ++i)
00343     {
00344       diagnostic_msgs::DiagnosticArray msg_out;
00345       diagnostic_updater::DiagnosticStatusWrapper stat;
00346 
00347       Device *device = Devices[i];
00348       const PowerMessage *pmesg = &device->getPowerMessage();
00349       
00350       ostringstream ss;
00351       ss << "Power board " << pmesg->header.serial_num;
00352       stat.name = ss.str();
00353 
00354       if( (ros::Time::now() - device->message_time) > TIMEOUT )
00355       {
00356         stat.summary(2, "No Updates");
00357       }
00358       else
00359       {
00360         stat.summary(0, "Running");
00361       }
00362       const StatusStruct *status = &pmesg->status;
00363 
00364       ROS_DEBUG("Device %u", i);
00365       ROS_DEBUG(" Serial       = %u", pmesg->header.serial_num);
00366 
00367       stat.add("Serial Number", pmesg->header.serial_num);
00368 
00369       ROS_DEBUG(" Current      = %f", status->input_current);
00370       stat.add("Input Current", status->input_current);
00371 
00372       ROS_DEBUG(" Voltages:");
00373       ROS_DEBUG("  Input       = %f", status->input_voltage);
00374       stat.add("Input Voltage", status->input_voltage);
00375       
00376       ROS_DEBUG("  DCDC 12     = %f", status->DCDC_12V_out_voltage);
00377       stat.add("DCDC12", status->DCDC_12V_out_voltage);
00378       
00379       ROS_DEBUG("  DCDC 15     = %f", status->DCDC_19V_out_voltage);
00380       stat.add("DCDC 15", status->DCDC_19V_out_voltage);
00381       
00382       ROS_DEBUG("  CB0 (Base)  = %f", status->CB0_voltage);
00383       stat.add("Breaker 0 Voltage", status->CB0_voltage);
00384       
00385       ROS_DEBUG("  CB1 (R-arm) = %f", status->CB1_voltage);
00386       stat.add("Breaker 1 Voltage", status->CB1_voltage);
00387       
00388       ROS_DEBUG("  CB2 (L-arm) = %f", status->CB2_voltage);
00389       stat.add("Breaker 2 Voltage", status->CB2_voltage);
00390 
00391       ROS_DEBUG(" Board Temp   = %f", status->ambient_temp);
00392       stat.add("Board Temperature", status->ambient_temp);
00393       
00394       ROS_DEBUG(" Fan Speeds:");
00395       ROS_DEBUG("  Fan 0       = %u", status->fan0_speed);
00396       stat.add("Fan 0 Speed", status->fan0_speed);
00397       
00398       ROS_DEBUG("  Fan 1       = %u", status->fan1_speed);
00399       stat.add("Fan 1 Speed", status->fan1_speed);
00400       
00401       ROS_DEBUG("  Fan 2       = %u", status->fan2_speed);
00402       stat.add("Fan 2 Speed", status->fan2_speed);
00403       
00404       ROS_DEBUG("  Fan 3       = %u", status->fan3_speed);
00405       stat.add("Fan 3 Speed", status->fan3_speed);
00406 
00407       ROS_DEBUG(" State:");
00408       ROS_DEBUG("  CB0 (Base)  = %s", cb_state_to_str(status->CB0_state));
00409       stat.add("Breaker 0 State", cb_state_to_str(status->CB0_state));
00410 
00411       ROS_DEBUG("  CB1 (R-arm) = %s", cb_state_to_str(status->CB1_state));
00412       stat.add("Breaker 1 State", cb_state_to_str(status->CB1_state));
00413 
00414       ROS_DEBUG("  CB2 (L-arm) = %s", cb_state_to_str(status->CB2_state));
00415       stat.add("Breaker 2 State", cb_state_to_str(status->CB2_state));
00416       
00417       ROS_DEBUG("  DCDC        = %s", master_state_to_str(status->DCDC_state));
00418       stat.add("DCDC state", master_state_to_str(status->DCDC_state));
00419 
00420       ROS_DEBUG(" Status:");
00421       ROS_DEBUG("  CB0 (Base)  = %s", (status->CB0_status) ? "On" : "Off");
00422       stat.add("Breaker 0 Status", (status->CB0_status) ? "On" : "Off");
00423       
00424       ROS_DEBUG("  CB1 (R-arm) = %s", (status->CB1_status) ? "On" : "Off");
00425       stat.add("Breaker 1 Status", (status->CB1_status) ? "On" : "Off");
00426       
00427       ROS_DEBUG("  CB2 (L-arm) = %s", (status->CB2_status) ? "On" : "Off");
00428       stat.add("Breaker 2 Status", (status->CB2_status) ? "On" : "Off");
00429       
00430       ROS_DEBUG("  estop_button= %x", (status->estop_button_status));
00431       stat.add("RunStop Button Status", status->estop_button_status);
00432       
00433       ROS_DEBUG("  estop_status= %x", (status->estop_status));
00434       stat.add("RunStop Status", status->estop_status);
00435 
00436       ROS_DEBUG(" Revisions:");
00437       ROS_DEBUG("         PCA = %c", status->pca_rev);
00438       stat.add("PCA", status->pca_rev);
00439 
00440       ROS_DEBUG("         PCB = %c", status->pcb_rev);
00441       stat.add("PCB", status->pcb_rev);
00442 
00443       ROS_DEBUG("       Major = %c", status->major_rev);
00444       stat.add("Major Revision", status->major_rev);
00445       
00446       ROS_DEBUG("       Minor = %c", status->minor_rev);
00447       stat.add("Minor Revision", status->minor_rev);
00448 
00449       stat.add("Min Voltage", status->min_input_voltage);
00450       stat.add("Max Current", status->max_input_current);
00451 
00452       const TransitionMessage *tmsg = &device->getTransitionMessage();
00453       for(int cb_index=0; cb_index < 3; ++cb_index)
00454       {
00455         const TransitionCount *trans = &tmsg->cb[cb_index];
00456         ROS_DEBUG("Transition: CB%d", cb_index);
00457         ss.str("");
00458         ss << "CB" << cb_index << " Stop Count";
00459         stat.add(ss.str(), trans->stop_count);
00460         
00461         ss.str("");
00462         ss << "CB" << cb_index << " E-Stop Count";
00463         stat.add(ss.str(), trans->estop_count);
00464         
00465         ss.str("");
00466         ss << "CB" << cb_index << " Trip Count";
00467         stat.add(ss.str(), trans->trip_count);
00468         
00469         ss.str("");
00470         ss << "CB" << cb_index << " 18V Fail Count";
00471         stat.add(ss.str(), trans->fail_18V_count);
00472       
00473         ss.str("");
00474         ss << "CB" << cb_index << " Disable Count";
00475         stat.add(ss.str(), trans->disable_count);
00476       
00477         ss.str("");
00478         ss << "CB" << cb_index << " Start Count";
00479         stat.add(ss.str(), trans->start_count);
00480       
00481         ss.str("");
00482         ss << "CB" << cb_index << " Pump Fail Count";
00483         stat.add(ss.str(), trans->pump_fail_count);
00484       
00485         ss.str("");
00486         ss << "CB" << cb_index << " Reset Count";
00487         stat.add(ss.str(), trans->reset_count);
00488       }
00489 
00490       msg_out.status.push_back(stat);
00491 
00492       //ROS_DEBUG("Publishing ");
00493       pub.publish(msg_out);
00494     }
00495 
00496   }
00497 }
00498 
00499 void sendMessages()
00500 {
00501   myBoard->sendDiagnostic();
00502 }
00503 
00504 void CloseAllDevices(void) {
00505   for (unsigned i=0; i<Devices.size(); ++i){
00506     if (Devices[i] != NULL) {
00507       delete Devices[i];
00508     }
00509   }
00510 }
00511 
00512 
00513 // Build list of interfaces
00514 int CreateAllInterfaces(void)
00515 {
00516   return 0;
00517 }
00518 
00519 void generateDeviceMessages()
00520 {
00521   static PowerMessage pm;
00522   
00523   pm.header.message_revision = CURRENT_MESSAGE_REVISION; //32 bit 
00524   pm.header.serial_num = 0xDADABABA;       //32 bit  Unique ID number
00525   //pm.header.text[32];         //Description identifier
00526   pm.header.message_id = MESSAGE_ID_POWER;
00527   pm.header.data_length = sizeof(pm.status);      //Length of the following structure
00528   
00529   /*pm.status.CB0_state;   // CB_State enum
00530   pm.status.CB1_state;
00531   pm.status.CB2_state;
00532   pm.status.DCDC_state;  // Master_State enum
00533   
00534   pm.status.input_voltage;
00535   pm.status.input_current;
00536   pm.status.DCDC_12V_out_voltage;
00537   pm.status.DCDC_19V_out_voltage;
00538   pm.status.CB0_voltage;
00539   pm.status.CB1_voltage;
00540   pm.status.CB2_voltage;
00541   pm.status.ambient_temp;
00542   pm.status.fan0_speed; 
00543   pm.status.fan1_speed;
00544   pm.status.fan2_speed;
00545   pm.status.fan3_speed;
00546   pm.status.CB0_status;  //0-off 1-on
00547   pm.status.CB1_status;
00548   pm.status.CB2_status;
00549   pm.status.estop_button_status;
00550   pm.status.estop_status;
00551   pm.status.pca_rev;
00552   pm.status.pcb_rev;
00553   pm.status.major_rev;
00554   pm.status.minor_rev;
00555   pm.status.min_input_voltage;
00556   pm.status.max_input_current;*/
00557 
00558   int i = 0;
00559   while (myBoard->ok())
00560   {
00561     i++;
00562     if (i % 200 < 100) // Alternate between sending and not sending messages to test the display.
00563     {
00564       myBoard->process_message(&pm);
00565       ROS_INFO("Sent message");
00566     }
00567     else
00568       ROS_INFO("Idling");
00569     ros::Duration(0,1e8).sleep();
00570   }
00571 }
00572 
00573 void processSentMessage(CommandMessage *cmd)
00574 {
00575   ROS_DEBUG("processSentMessage");
00576 }
00577 
00578 int main(int argc, char** argv)
00579 {
00580   ros::init(argc, argv);
00581 
00582   CreateAllInterfaces();
00583   myBoard = new PowerBoard();
00584 
00585   boost::thread sendThread( &sendMessages );
00586   boost::thread fakeDeviceThread( &generateDeviceMessages );
00587 
00588   myBoard->spin(); //wait for ros to shut us down
00589 
00590   sendThread.join();
00591 
00592   CloseAllDevices();
00593 
00594   
00595   delete myBoard;
00596   return 0;
00597 
00598 }


pr2_power_board
Author(s): Curt Meyers, Blaise Gassend
autogenerated on Sat Dec 28 2013 17:27:00