Note: Goby version 1 (shown here) is now considered obsolete. Please use version 2 for new projects, and consider upgrading old projects.

Goby Underwater Autonomy Project  Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
moos/iCommander/command_gui.cpp
00001 #include "command_gui.h"
00002 #include "iCommander.h"
00003 
00004 using namespace std;
00005 using boost::trim_copy;
00006 
00007 using namespace goby::acomms;
00008 using goby::util::as;
00009 
00010 //using namespace boost::posix_time;
00011 
00012 // Construction / Destruction
00013 CommandGui::CommandGui()
00014     : initialized_(false),
00015       refresh_(false),
00016       curr_mess_vals_(NULL),
00017       screen_initialized_(false),
00018       inserting_x_(false),
00019       inserting_y_(false)
00020 { }
00021 
00022 CommandGui::~CommandGui()
00023 { }
00024 
00025 bool CommandGui::run()
00026 {
00027     while(1)
00028     {
00029         if(initialized_)
00030             do_run(); // do the real loop
00031         else
00032             sleep(1); // wait for iCommander's startup and read to finish
00033     }
00034     return true;
00035 }
00036 
00037 void CommandGui::do_run()
00038 {
00039     if(!screen_initialized_)
00040     {            
00041         CiCommander::gui_.refresh();
00042         state_ = s_menu;
00043         screen_initialized_ = true;
00044     }
00045         
00046     switch (state_)
00047     {
00048         default:
00049         case s_menu:
00050             main_menu();            
00051             break;
00052                 
00053         case s_new:
00054             new_message();
00055             break;
00056                 
00057         case s_open:
00058             open_message();
00059             break;
00060                 
00061         case s_save:
00062             save_message();
00063             break;
00064                 
00065         case s_import:
00066             import_xml();
00067             break;
00068                 
00069         case s_exit:
00070             CiCommander::gui_.cleanup();
00071             exit(EXIT_SUCCESS);            
00072             break;
00073                 
00074         case s_editing:
00075             edit();
00076             break;
00077                 
00078         case s_preview:
00079             preview();
00080             break;
00081                 
00082         case s_send:
00083             send();
00084             break;
00085                 
00086         case s_quick_switch:
00087             quick_switch();
00088             state_ = s_editing;
00089             break;
00090                 
00091         case s_clear:
00092             clear_message();
00093             state_ = s_editing;
00094             break;
00095     }
00096 }
00097 
00098 void CommandGui::main_menu()
00099 {
00100 
00101     string title = string("<C></B>iCommander: Vehicle Command Message Sender\n") +
00102         string("<C> (C) 2009 t. schneider tes@mit.edu \n") +
00103         string("<C>") + boost::lexical_cast<std::string>(CiCommander::dccl_.message_count()) + string(" message(s) loaded.\n") +
00104         string("</B/40>Main Menu:<!40>");
00105 
00106 //    for(int i = 0; i < 64; ++i)
00107 //                title += "</" + as<string>(i) + ">" +  as<string>(i) + "<!"+ as<string>(i) + ">";
00108     
00109 
00110     vector<string> buttons;
00111     if (curr_mess_vals_ != NULL && !curr_mess_vals_->empty())
00112         buttons.push_back("Return to active message");
00113     buttons.push_back("Select Message");
00114     buttons.push_back("Load");
00115     buttons.push_back("Save");    
00116     buttons.push_back("Import Message File (XML)");
00117     buttons.push_back("Exit");
00118 
00119     int selection;
00120     CiCommander::gui_.disp_scroll(title, buttons, selection);
00121     
00122     
00123     if (curr_mess_vals_ == NULL || curr_mess_vals_->empty())
00124         state_ = (state)selection;
00125     else
00126         state_ = (selection == 0) ? s_editing : (state)(selection - 1);
00127 }
00128 
00129 
00130 void CommandGui::new_message()
00131 {
00132     if(!CiCommander::dccl_.message_count())
00133     {
00134         CiCommander::gui_.disp_scroll(string("No messages defined. Import a message file or specify them in the moos file"), vector<string>(1, "OK"));
00135         state_ = s_menu;
00136         return;
00137     }
00138         
00139     string title = "</B/40>Available Messages<!40> (can use tab completion):";
00140     string label = "Message to create: ";
00141     vector<string> items;
00142     
00143     for (vector<DCCLMessage>::iterator it = CiCommander::dccl_.messages().begin(), n = CiCommander::dccl_.messages().end(); it != n; ++it)
00144         items.push_back(it->name());
00145 
00146     int selected;
00147     if(CiCommander::gui_.disp_alphalist(title, label, items, selected))
00148     {
00149         curr_mess_vals_ = &open_mess_vals_[selected];
00150         curr_message_ = &CiCommander::dccl_.messages()[selected];
00151         state_ = s_editing;
00152     }
00153     else
00154     {
00155         state_ = s_menu;
00156     }
00157 }
00158 
00159 void CommandGui::open_message()
00160 {    
00161     string title = "Choose a file to open (ESC to cancel):";
00162     string label = "File name: ";
00163     string filename;
00164 
00165     if(CiCommander::gui_.disp_fselect(title, label, filename) && do_open(filename))
00166         CiCommander::gui_.disp_scroll(string("File read in:\n" + filename), vector<string>(1, "OK"));        
00167     
00168     state_ = s_menu;
00169 }
00170 
00171 void CommandGui::save_message()
00172 {
00173     string title = "Choose a file to save to (ESC to cancel):";
00174     string label = "File name: ";
00175 
00176     string filename;
00177 
00178     if(CiCommander::gui_.disp_fselect(title, label, filename))
00179     {        
00180        
00181         vector<string> v;
00182         v.push_back("Save");
00183         v.push_back("Cancel");
00184         int selection;
00185         CiCommander::gui_.disp_scroll(string("Confirm save message to:\n" + filename), v, selection);    
00186         if(selection == 0)
00187         {
00188             if(do_save(filename))
00189             {
00190                 CiCommander::gui_.disp_scroll(string("Message saved to:\n" + filename), vector<string>(1, "OK"));
00191             }
00192         }
00193     }
00194 
00195     state_ = s_menu;    
00196 }
00197 
00198 bool CommandGui::do_save(string filename)
00199 {
00200     ofstream fout;
00201     fout.open(filename.c_str());
00202 
00203     vector<string> * save_curr_mess_vals;
00204     DCCLMessage * save_curr_message;
00205 
00206     if(fout.is_open())
00207     {
00208         string date = MOOSGetDate();
00209         fout << "%%% iCommander message save file: " << date.substr(0,date.length()-1) << " %%%" << endl;
00210         
00211         for(std::map<int, std::vector<std::string> >::iterator it = open_mess_vals_.begin(), n = open_mess_vals_.end(); it != n; ++it)
00212         {
00213             save_curr_mess_vals = &(it->second);
00214             save_curr_message = &CiCommander::dccl_.messages()[it->first];
00215             fout << "message::" << save_curr_message->name() << endl;
00216             std::map<std::string, std::string> vals;
00217             vector<string> unused;
00218             prepare_message(vals, unused, false, save_curr_mess_vals, save_curr_message);
00219             for(map<string, string>::const_iterator it = vals.begin(); it != vals.end(); ++it)
00220             {
00221                 string variable = it->first;
00222                 string contents = it->second;
00223               
00224                 if(it != vals.begin())
00225                     fout << " ";
00226               
00227                 fout << variable << ":=" << contents;
00228             }
00229             fout << endl;
00230           
00231         }
00232         fout.close();
00233         return true;
00234       
00235     }
00236   
00237     return false;
00238 }
00239 
00240 bool CommandGui::do_open(string filename)
00241 {
00242     vector<string> * open_curr_mess_vals = NULL;
00243     DCCLMessage * open_curr_message = NULL;
00244 
00245     ifstream fin;
00246     fin.open(filename.c_str());
00247     
00248     if(fin.is_open())
00249     {
00250         string line;
00251         getline(fin, line);
00252         string::size_type pos = line.find("%%% iCommander message save file: ");
00253         if(pos == string::npos)
00254         {
00255             if(CiCommander::gui_.initialized())
00256                 CiCommander::gui_.disp_scroll(string("Invalid file:\n" + filename), vector<string>(1, "OK"));
00257             state_ = s_menu;
00258             return false;
00259         }
00260         
00261         while(getline(fin,line))
00262         {
00263             pos = line.find("message::");
00264             if(pos != string::npos)
00265             {
00266                 string new_message = line.substr(string("message::").length());
00267                 
00268                 for(vector<DCCLMessage>::size_type i = 0, n = CiCommander::dccl_.messages().size(); i < n; ++i)
00269                 {
00270                     if(new_message == CiCommander::dccl_.messages()[i].name())
00271                     {
00272                         open_curr_mess_vals = &open_mess_vals_[i];
00273                         open_curr_message = &CiCommander::dccl_.messages()[i];
00274                         open_curr_mess_vals->clear();
00275                     }
00276                 }
00277             }
00278             else if(open_curr_mess_vals != NULL && open_curr_message != NULL)
00279             {    
00280                 //replace spaces with commas
00281                 string::size_type pos2 = line.find(" ");
00282                 while(pos2 != string::npos)
00283                 {
00284                     line[pos2] = ',';
00285                     pos2 = line.find(" ");
00286                 }
00287               
00288                 vector< boost::shared_ptr<DCCLMessageVar> > message_vars =
00289                     fetch_message_vars(open_curr_message);
00290 
00291                 for (vector< boost::shared_ptr<DCCLMessageVar> >::size_type i = 0, n = message_vars.size(); i < n; ++i)
00292                 {
00293                     boost::shared_ptr<DCCLMessageVar> mp = message_vars.at(i);
00294                     string piece;
00295                     if(MOOSValFromString(piece, line, mp->name()))
00296                         open_curr_mess_vals->push_back(piece);
00297                 }
00298             }
00299         }
00300       
00301         
00302         fin.close();
00303         return true;
00304     }
00305     return false;
00306 }
00307 
00308 void CommandGui::import_xml()
00309 {
00310     string title = "Choose an xml message file to import (ESC to cancel):";
00311     string label = "File name: ";
00312 
00313     string filename;
00314     if(CiCommander::gui_.disp_fselect(title, label, filename))
00315     {
00316         
00317         
00318         try
00319         {
00320             protobuf::DCCLConfig cfg;
00321             cfg.add_message_file()->set_path(filename);
00322             CiCommander::dccl_.merge_cfg(cfg);
00323             CiCommander::gui_.disp_scroll(string("Imported:\n" + filename), vector<string>(1, "OK"));
00324         }
00325         catch(exception & e)
00326         {
00327             CiCommander::gui_.disp_scroll(string("Invalid xml file:\n" + filename), vector<string>(1, "OK"));
00328         }
00329     }
00330 
00331     state_ = s_menu;
00332 }
00333 
00334 void CommandGui::edit()
00335 {
00336     // actual columns / rows
00337     vector< boost::shared_ptr<DCCLMessageVar> > message_vars =
00338         fetch_message_vars(curr_message_);
00339         
00340     int rows = message_vars.size();
00341     int cols = 1;
00342 
00343     curr_mess_vals_->resize(cols*rows);
00344     
00345     string title = "</B/40>Message<!40> (Type: " + curr_message_->name() + ")\n" +
00346         as<string>(message_vars.size()) + " entries total\n"+
00347         "\t{Enter} for options\n" +
00348         "\t{Up/Down} for more message variables";
00349     
00350     
00351     int maxwidth = 0;    
00352     rowtitle_.clear();
00353     coltitle_.clear();
00354 
00355     for (vector< boost::shared_ptr<DCCLMessageVar> >::size_type i = 0, n = message_vars.size(); i < n; ++i)
00356     {
00357         boost::shared_ptr<DCCLMessageVar> mp = message_vars.at(i);
00358 
00359         DCCLType type = mp->type();
00360         std::string stype;
00361         
00362         int width;
00363         switch(type)
00364         {
00365             case dccl_string:
00366                 stype = "</24>string<!24>";
00367                 width = mp->max_length();
00368                 break;
00369             case dccl_bool:
00370                 stype = "</32>bool<!32>";
00371                 width = 4;
00372                 break;
00373             case dccl_float:
00374                 stype = "</48>float<!48>";
00375                 // + 1 for decimal point, +1 for e
00376                 width =
00377                     max((as<string>((int)mp->max())).size(),(as<string>((int)mp->min())).size()) + mp->precision() + 2;
00378                 break;
00379             case dccl_int:
00380                 stype = "</56>int<!56>";
00381                 width = max((as<string>((int)mp->max())).size(),(as<string>((int)mp->min())).size());
00382                 break;
00383                 
00384             case dccl_static:
00385                 stype = "static";
00386                 width = mp->static_val().size();
00387                 break;
00388                 
00389             case dccl_enum:
00390                 stype = "</40>enum<!40>";
00391                 width = 0;
00392                 vector<string>* enums = mp->enums();
00393                 for (vector<string>::size_type j = 0, m = enums->size(); j < m; ++j)
00394                     width = max((int)enums->at(j).size(), width);
00395                 break;
00396                     
00397         }
00398         
00399         maxwidth = max(maxwidth, width);
00400 
00401         string t = "</B/24>" + boost::lexical_cast<std::string>(i+1) + "<!B!24>" +
00402             "/" + boost::lexical_cast<std::string>(n) + ". " + mp->name() + " (" + stype + ")";
00403 
00404         rowtitle_.push_back(t);        
00405     }
00406 
00407     
00408     // account for specials
00409     maxwidth = max(maxwidth, (int)string("_time").length());
00410     maxwidth = max(maxwidth, (int)string("_x(lon)10:10000").length());
00411     
00412     // set static values
00413     for (vector< boost::shared_ptr<DCCLMessageVar> >::size_type i = 0, n = message_vars.size(); i < n; ++i)
00414     {
00415         boost::shared_ptr<DCCLMessageVar> mp = message_vars.at(i);
00416         DCCLType type = mp->type();
00417         
00418         if (type == dccl_static)
00419             curr_mess_vals_->at(i) = mp->static_val();            
00420     
00421     }
00422 
00423     // (boost::bind allows us to use a member function of CommandGui
00424     // as a callback to the C coded CDK)
00425     cdk_callback pre =
00426         boost::bind(&CommandGui::edit_precallback, this, _1, _2, _3, _4);
00427 
00428     cdk_callback post =
00429         boost::bind(&CommandGui::edit_postcallback, this, _1, _2, _3, _4);
00430 
00431     int startx = CENTER;
00432     int starty = 4;
00433 
00434     CiCommander::gui_.disp_matrix(title,
00435                      rows,
00436                      cols,
00437                      maxwidth,
00438                      rowtitle_,
00439                      coltitle_,
00440                      *curr_mess_vals_,
00441                      pre,
00442                      post,
00443                      startx,
00444                      starty);
00445     CiCommander::gui_.cursor_off();    
00446 }
00447 
00448 int CommandGui::edit_popup()
00449 {   
00450     string title = (string)"<C> Choose an action";
00451     vector<string> buttons;
00452 
00453     buttons.push_back("Return to message");
00454     buttons.push_back("</B>Send");
00455     buttons.push_back("Preview");
00456     buttons.push_back("Quick switch to another open message");
00457     buttons.push_back("</B/40>Main Menu<!40>");
00458     buttons.push_back("Insert special: current time [shortcut: t]");
00459     buttons.push_back("Insert special: UTM x,y to lon,lat [shortcut: x]");
00460     buttons.push_back("Insert special: community");
00461     buttons.push_back("Insert special: modem id [shortcut: m]");
00462     buttons.push_back("Clear message");
00463     buttons.push_back("Fix resized screen [shortcut: ESC]");
00464     
00465     int selection;
00466     CiCommander::gui_.disp_scroll(title, buttons, selection);
00467     
00468     return selection;
00469 }
00470 
00471 bool CommandGui::edit_precallback(int row, int col, string & val, chtype & input)
00472 {
00473     if(CiCommander::cfg_.force_xy_only())
00474     {
00475         for(vector<string>::size_type i = 0, n = rowtitle_.size(); i < n; ++i)
00476         {
00477             string aval;
00478             CiCommander::gui_.get_matrix_val(i+1, 1, aval);
00479             string::size_type aposy = aval.find("_y:");
00480             string::size_type aposx = aval.find("_x:");
00481             if(aposy == string::npos && aposx == string::npos)
00482             {
00483                 int lat_row, lon_row;
00484                 find_lat_lon_rows(i+1, lat_row, lon_row);
00485                 if(lat_row && lon_row)
00486                 {
00487                     CiCommander::gui_.set_matrix_val(lon_row, 1, "_x:");
00488                     CiCommander::gui_.set_matrix_val(lat_row, 1, "_y:");
00489                 }
00490             }
00491         }
00492     }
00493     
00494     CiCommander::gui_.cursor_off();
00495 
00496     vector< boost::shared_ptr<DCCLMessageVar> > message_vars =
00497         fetch_message_vars(curr_message_);
00498     
00499     boost::shared_ptr<DCCLMessageVar> mp = message_vars.at(row-1);
00500 
00501     DCCLType type = mp->type();
00502 
00503     // check legality of input
00504     if(input == KEY_ENTER)
00505         return edit_menu(row, val, type);
00506     else if(isprint(input))
00507         return edit_isprint(row, val, input, type, mp);
00508 //    else if(input == KEY_ESC)
00509 //        return false;
00510     else if(input == KEY_DC || input == KEY_BACKSPACE)
00511         return edit_backspace(row, val, type);
00512     else if(input == KEY_LEFT || input == KEY_RIGHT)
00513         return edit_leftright(row, val, input, type, mp);    
00514         
00515     return true;
00516 }
00517 
00518 
00519 bool CommandGui::edit_menu(int row, std::string & val, DCCLType type)
00520 {
00521     switch(edit_popup())
00522     {
00523         default:
00524         case 0: // return to message
00525             return false;
00526             break;
00527 
00528         case 1: // send
00529             state_ = s_send;
00530             break;
00531                 
00532         case 2: //preview
00533             state_ = s_preview;
00534             break;
00535 
00536         case 3: // quick switch
00537             state_ = s_quick_switch;
00538             break;
00539                 
00540                 
00541         case 4: // main menu
00542             state_ = s_menu;
00543             break;
00544 
00545                     
00546         case 5: // insert special: time
00547             if (type == dccl_int || type == dccl_string || type == dccl_float)
00548                 check_specials(val, 't', row);
00549             return false;
00550             break;
00551                 
00552         case 6: // insert special: LocalUTM2LatLong
00553             check_specials(val, 'y', row);                
00554             return false;
00555             break;
00556 
00557                 
00558         case 7: // insert special: community
00559             if (type == dccl_string)
00560                 val = CiCommander::cfg_.common().community();
00561             return false;
00562             break;
00563                 
00564         case 8: // insert special: modem id
00565             if (type == dccl_int || type == dccl_string || type == dccl_float)
00566                 check_specials(val, 'm', row);
00567                 
00568             return false;
00569             break;
00570                 
00571         case 9: // clear
00572             state_ = s_clear;
00573             break;
00574 
00575         case 10: //resize
00576             CiCommander::gui_.resize();
00577             break;
00578                     
00579     }        
00580     return true;
00581 }
00582 
00583 bool CommandGui::edit_isprint(int row, std::string & val, chtype input, DCCLType type, boost::shared_ptr<DCCLMessageVar> mp)
00584 {       
00585     string::size_type posy = val.find("_y:");
00586     string::size_type posx = val.find("_x:");
00587 
00588     if(type == dccl_string)
00589     {
00590         // can't enter string longer than max_length
00591         if(val.size() == (unsigned int)mp->max_length())
00592             return false;
00593     }
00594     else if (type == dccl_bool)
00595     {
00596         return false;
00597     }
00598     else if (type == dccl_float)
00599     {
00600         //must be a number or decimal place or exponential
00601         if(!isdigit(input) && input != '-' && input != '.' && input != 'e')
00602         {
00603             check_specials(val, input, row);
00604                 
00605             return false;
00606         }
00607         else
00608         {
00609             // - sign at start only
00610             if(input == '-' && (((val.size() != 0) || mp->min() >= 0) && posy == string::npos && posx == string::npos))
00611                 return false;
00612                 
00613             if(input == '.')
00614             {
00615                 for (unsigned int j = 0; j < val.size(); j++)
00616                 {
00617                     if(val[j]=='.')
00618                         return false;
00619                 }
00620             }
00621                 
00622             // check precisions
00623             string snum = val + (char)input;
00624                 
00625             double dnum = atof(snum.c_str());
00626 
00627                 
00628             if(mp->precision() >= 0)
00629             {
00630                 double a = goby::util::unbiased_round(fabs(dnum)*pow(10.0, (double)mp->precision()),0);
00631                 double b = fabs(dnum)*pow(10.0, (double)mp->precision());
00632                 if(as<string>(a) != as<string>(b))
00633                 {
00634  
00635                     return false;
00636                 }
00637                     
00638             }
00639                 
00640             // check max, min
00641             if((dnum >0 && dnum > mp->max()) || (dnum < 0 && dnum < mp->min()))
00642                 return false;
00643         }            
00644     }
00645     else if (type == dccl_int)
00646     {
00647         if(!isdigit(input) && input != '-')
00648         {
00649             check_specials(val, input, row);
00650             return false;
00651         }
00652             
00653         else
00654         {
00655             // - sign at start only
00656             if(input == '-' && (((val.size() != 0) || mp->min() >= 0) && posy == string::npos && posx == string::npos))
00657                 return false;
00658 
00659             string snum = val + (char)input;
00660             double dnum = atof(snum.c_str());
00661             // check max, min
00662             if((dnum >0 && dnum > mp->max()) || (dnum < 0 && dnum < mp->min()))
00663                 return false;
00664 
00665         }
00666     }
00667     else if (type == dccl_enum)
00668     {
00669         return false;
00670     }
00671     else if (type == dccl_static)
00672     {
00673         return false;    
00674     }
00675 
00676     return true;
00677 }
00678 
00679 bool CommandGui::edit_backspace(int row, std::string & val, DCCLType type)
00680 {
00681     string::size_type posy = val.find("_y:");
00682     string::size_type posx = val.find("_x:");
00683 
00684     if(type == dccl_static)
00685         return false;
00686     else if(type == dccl_bool || type == dccl_enum)
00687     {
00688         val = "";
00689         return false;
00690     }
00691     else if(type == dccl_int || type == dccl_float)
00692     {
00693         if(val == "_time")
00694         {
00695             val = "";
00696             return false;
00697         }
00698             
00699         if(posy != string::npos || posx != string::npos)
00700         {
00701             if (val.at(val.length()-1) == ':')
00702             {
00703                 int lat_row, lon_row;
00704                 find_lat_lon_rows(row, lat_row, lon_row);
00705                 if(!CiCommander::cfg_.force_xy_only())
00706                     val = "";
00707                 if(lat_row && lon_row && !CiCommander::cfg_.force_xy_only())
00708                 {
00709                     CiCommander::gui_.set_matrix_val(lon_row, 1, "");
00710                     CiCommander::gui_.set_matrix_val(lat_row, 1, "");
00711                 }                        
00712                 return false;
00713             }
00714                 
00715         }
00716     }
00717     return true;
00718 }
00719 
00720 bool CommandGui::edit_leftright(int row, std::string & val, chtype input, DCCLType type, boost::shared_ptr<DCCLMessageVar> mp)
00721 {
00722     string::size_type posy = val.find("_y:");
00723     string::size_type posx = val.find("_x:");
00724 
00725     if (type == dccl_bool || type == dccl_enum)
00726     {                
00727         string value;
00728             
00729         if(type == dccl_bool)
00730         {
00731             value = (val== "true") ? "false" : "true";
00732         }
00733         else
00734         {
00735             size_t curr_enum_pos = 0;
00736             vector<string>* enums = mp->enums();
00737             for (vector<string>::size_type j = 0, m = enums->size(); j < m; ++j)
00738                 curr_enum_pos = (enums->at(j) == val) ? j : curr_enum_pos;
00739 
00740             if (input == KEY_LEFT)
00741             {
00742                 curr_enum_pos = (curr_enum_pos == 0) ? enums->size()-1 : curr_enum_pos-1;
00743             }
00744             else
00745             {
00746                 curr_enum_pos = (curr_enum_pos == enums->size()-1) ? 0 : curr_enum_pos+1;
00747             }
00748             value = enums->at(curr_enum_pos);
00749                 
00750         }
00751 
00752         val = value;
00753     }
00754 
00755     if (type == dccl_float || type == dccl_int)
00756     {
00757         string snum;
00758             
00759         if(posy != string::npos || posx != string::npos)
00760         {
00761             string::size_type col_pos = val.find(':');
00762             snum = val.substr(col_pos+1);
00763         }
00764         else
00765         {
00766             snum = val;
00767         }
00768 
00769         double dnum = atof(snum.c_str());
00770 
00771         if (input == KEY_LEFT)
00772         {
00773             dnum = dnum-1;
00774         }
00775         else
00776         {
00777             dnum = dnum+1;
00778         }
00779             
00780         if(posy == string::npos && posx == string::npos)
00781         {    
00782             if(dnum > mp->max())
00783                 dnum = mp->max();
00784             if(dnum < mp->min())
00785                 dnum = mp->min();
00786         }
00787             
00788             
00789         if(posy != string::npos)
00790             val = "_y:" + as<string>(dnum);
00791         else if(posx != string::npos)
00792             val = "_x:" + as<string>(dnum);
00793         else
00794             val = as<string>(dnum);
00795     }
00796 
00797     return false;
00798 
00799 }
00800 
00801 
00802 bool CommandGui::edit_postcallback(int row, int col, string & val, chtype & input)
00803 {
00804     if(input == KEY_RESIZE)
00805     {
00806         vector<string> a;
00807         a.push_back("resize");
00808         CiCommander::gui_.disp_lower_info(a);
00809 
00810         return false;
00811     }
00812     vector< boost::shared_ptr<DCCLMessageVar> > message_vars =
00813         fetch_message_vars(curr_message_);
00814 
00815     boost::shared_ptr<DCCLMessageVar> mp = message_vars.at(row-1);
00816 
00817     vector<string> mesg;
00818     
00819     string line = "Editing message variable " + as<string>(row) + " of " + as<string>(message_vars.size()) + ": " + mp->name();
00820     mesg.push_back(line);
00821     
00822     DCCLType type = mp->type();
00823     
00824     if(type == dccl_string)
00825         line = "(</24>string<!24>) max length: " + as<string>(mp->max_length()) + " chars.";
00826     else if (type == dccl_bool)
00827         line = "(</32>bool<!32>) specify true/false with right/left arrows";
00828     else if (type == dccl_float)
00829         line = "(</48>float<!48>) min: " + as<string>(mp->min()) + ", max: " + as<string>(mp->max()) + ", precision: " + as<string>(mp->precision());
00830     else if (type == dccl_int)
00831         line = "(</56>int<!56>) min: " + as<string>(mp->min()) + ", max: " + as<string>(mp->max());    
00832     else if (type == dccl_enum)
00833         line = "(</40>enum<!40>) specify values with right/left arrows";
00834     else if (type == dccl_static)
00835         line = "(static) you cannot change the value of this field";
00836 
00837     if(type == dccl_string || type == dccl_float || type == dccl_int)
00838         CiCommander::gui_.cursor_on();
00839     
00840     mesg.push_back(line);
00841     
00842     CiCommander::gui_.disp_info(mesg);
00843 
00844     return true;
00845 }
00846 
00847 
00848 void CommandGui::prepare_message(map<string, string> & out_vals, vector<string> & out_of_range, bool do_substitutes, std::vector<std::string> * curr_vals, DCCLMessage * curr_message)
00849 {
00850     vector<string> copy_vals;
00851     
00852     if(curr_vals == NULL)
00853         copy_vals = (*curr_mess_vals_);
00854     else
00855         copy_vals = (*curr_vals);
00856     
00857     vector< boost::shared_ptr<DCCLMessageVar> > message_vars = (curr_message)
00858         ? fetch_message_vars(curr_message)
00859         : fetch_message_vars(curr_message_);
00860     
00861     if(do_substitutes)
00862     {
00863         string long_string;
00864         
00865         for(vector<string>::size_type i = 0, n = copy_vals.size(); i < n; ++i)
00866             long_string += copy_vals.at(i) + ",";
00867         
00868         insert_specials(long_string);
00869         
00870         boost::split(copy_vals, long_string, boost::is_any_of(","));
00871     }    
00872     
00873     for (vector< boost::shared_ptr<DCCLMessageVar> >::size_type i = 0, n = message_vars.size(); i < n; ++i)
00874     {
00875         boost::shared_ptr<DCCLMessageVar> mp = message_vars.at(i);
00876         map<string, string>::iterator it = out_vals.find(mp->source_var());
00877         if(it != out_vals.end())
00878             out_vals[mp->source_var()] += ",";
00879         
00880         string curr_val = copy_vals.at(i);
00881         
00882         if(do_substitutes)
00883         {
00884             if(!field_check(curr_val, mp))
00885                 out_of_range.push_back(mp->name());    
00886         }
00887         
00888         
00889         out_vals[mp->source_var()] += mp->name() + "=" + curr_val;
00890     }    
00891     
00892 }
00893 
00894 void CommandGui::preview()
00895 {
00896     string mesg = "</B/40>Previewing message:<!40>\n";
00897 
00898     assemble_message_display(mesg);
00899     
00900     vector<string> buttons;
00901     buttons.push_back("Return to message");
00902     buttons.push_back("Send");
00903 
00904     int selection;
00905     CiCommander::gui_.disp_scroll(mesg, buttons, selection, true);
00906     if (selection == 1)
00907         state_ = s_send;
00908     else
00909         state_ = s_editing;
00910 }
00911 
00912 void CommandGui::send()
00913 {
00914     string mesg = "</B/40>Message(s) sent at time:<!40> " + microsec_simple_time_of_day() + "\n";
00915     if(do_save("iCommander_autosave.txt"))
00916         mesg += "</B>Autosaved to iCommander_autosave.txt\n";
00917 
00918     assemble_message_display(mesg);
00919 
00920     vector<string> buttons;
00921     buttons.push_back("Return to message");
00922     buttons.push_back("Main menu");
00923 
00924     int selection;
00925     CiCommander::gui_.disp_scroll(mesg, buttons, selection, true);
00926     if (selection == 0)
00927         state_ = s_editing;
00928     else
00929         state_ = s_menu;    
00930 }
00931 
00932 
00933 void CommandGui::assemble_message_display(std::string & mesg)
00934 {
00935     map<string, string> vals;
00936     vector<string> out_of_range;
00937     prepare_message(vals, out_of_range);    
00938 
00939     for(map<string, string>::const_iterator it = vals.begin(), n = vals.end(); it != n; ++it)
00940     {
00941         const string & variable = it->first;
00942         const string & contents = it->second;
00943         
00944         CiCommander::get_instance()->publish(variable, contents);
00945         mesg += "</56>" + variable + "<!56>" + ": " + contents + "\n";
00946     }
00947 
00948     if(!out_of_range.empty())
00949     {
00950         
00951         mesg += "\n</16>(Warning)<!16> These message vars are out of range (not specified or NaN):\n";
00952 
00953         for(vector<string>::const_iterator it = out_of_range.begin(), n = out_of_range.end(); it != n; ++it)
00954         {
00955             const string & variable = *it;
00956             if(it != out_of_range.begin())
00957                 mesg += ", ";
00958             mesg += variable;
00959         }
00960         mesg += "\n";
00961     }
00962     
00963 }
00964 
00965 void CommandGui::insert_specials(string & s)
00966 {
00967     string to_find;
00968     string::size_type pos;
00969 
00970     to_find = "_time";
00971     pos = s.find(to_find);
00972     while(pos != string::npos)
00973     {
00974         s = s.substr(0, pos) + as<string>(MOOSTime()) + s.substr(pos+to_find.length());
00975         pos = s.find(to_find);
00976     }
00977 
00978     string::size_type
00979         y_start_pos,
00980         x_start_pos,
00981         y_comma_pos,
00982         x_comma_pos;
00983 
00984     y_start_pos = s.find("_y:");
00985     x_start_pos = s.find("_x:");
00986     
00987     while(y_start_pos!=string::npos && x_start_pos!=string::npos)
00988     {
00989         y_comma_pos = s.find(",", y_start_pos);
00990         x_comma_pos = s.find(",", x_start_pos);
00991         
00992         string yval = s.substr(y_start_pos + 3, y_comma_pos-y_start_pos);
00993         string xval = s.substr(x_start_pos + 3, x_comma_pos-x_start_pos);
00994 
00995         double dyval = atof(yval.c_str());
00996         double dxval = atof(xval.c_str());
00997 
00998         double lat, lon;
00999 
01000         CiCommander::geodesy_.UTM2LatLong(dxval, dyval, lat, lon);
01001 
01002         
01003         // order matters
01004         if(y_start_pos < x_start_pos)
01005         {
01006             s.replace(x_start_pos, x_comma_pos-x_start_pos, as<string>(lon));
01007             s.replace(y_start_pos, y_comma_pos-y_start_pos, as<string>(lat));
01008         }
01009         else
01010         {
01011             s.replace(y_start_pos, y_comma_pos-y_start_pos, as<string>(lat));
01012             s.replace(x_start_pos, x_comma_pos-x_start_pos, as<string>(lon));
01013         }
01014         
01015         y_start_pos = s.find("_y:");
01016         x_start_pos = s.find("_x:");
01017     }    
01018 }
01019 
01020 int CommandGui::insert_modem_id()
01021 {
01022     string title = "Choose vehicle to insert id for:";
01023     vector<string> buttons;
01024 
01025     for(int i = 0,  n = CiCommander::modem_lookup_.max_id(); i <= n; ++i)
01026     {
01027         buttons.push_back(
01028             string(
01029                 as<string>(i) + ": " +
01030                 CiCommander::modem_lookup_.get_name_from_id(i) +
01031                 " (" + CiCommander::modem_lookup_.get_type_from_id(i) + ")"
01032                 )
01033             );
01034     }
01035     
01036     int selection;
01037     CiCommander::gui_.disp_scroll(title, buttons, selection);
01038     return selection;
01039 }
01040 
01041 // here we decide how to handle any oversized fields
01042 // we make the following choices:
01043 // truncate strings
01044 // fix float / integer to NaN if not specified or exceed
01045 // static / bools / enums cannot be a problem
01046 bool CommandGui::field_check(string & s, boost::shared_ptr<DCCLMessageVar> mp)
01047 {
01048     bool ok = s.empty() ? false : true;
01049     
01050     DCCLType type = mp->type();
01051     
01052     if (type == dccl_float || type == dccl_int)
01053     {
01054         // check precisions
01055         string snum = s;
01056         double dnum = atof(snum.c_str());
01057 
01058         if (type == dccl_float)
01059             dnum = goby::util::unbiased_round(dnum, mp->precision());
01060         
01061         // check max, min
01062         if(dnum > mp->max() || dnum < mp->min() || s.empty())
01063         {
01064             dnum = goby::acomms::NaN;
01065             ok = false;
01066         }        
01067             
01068         s = as<string>(dnum);
01069     }
01070     else if (type == dccl_string && ((int)s.length() > mp->max_length()))
01071     {
01072         ok = false;
01073         s = s.substr(0,mp->max_length());
01074     }
01075 
01076     return ok;
01077 }
01078 
01079 void CommandGui::quick_switch()
01080 {
01081     string title = "Choose an active message to switch to:";
01082     vector<string> buttons;
01083 
01084     vector<int> mapping;
01085 
01086     for(std::map<int, std::vector<std::string> >::iterator it = open_mess_vals_.begin(), n = open_mess_vals_.end(); it != n; ++it)
01087     {
01088         buttons.push_back(
01089             CiCommander::dccl_.messages().at(it->first).name()
01090             );
01091 
01092         mapping.push_back(it->first);
01093     }
01094 
01095     int selected;
01096     CiCommander::gui_.disp_scroll(title, buttons, selected);
01097 
01098     curr_mess_vals_ = &open_mess_vals_[mapping.at(selected)];
01099     curr_message_ = &CiCommander::dccl_.messages()[mapping.at(selected)];
01100 }
01101 
01102 void CommandGui::check_specials(string & val, chtype input, int row)
01103 {
01104     if(input == 't')
01105     {
01106         val = "_time";
01107     }
01108     else if(input == 'y' || input == 'x')
01109     {
01110         int lat_row;
01111         int lon_row; 
01112         find_lat_lon_rows(row, lat_row, lon_row);
01113         
01114         if(lat_row && lon_row)
01115         {
01116             if(lat_row == row)
01117             {
01118                 val = "_y:";
01119                 CiCommander::gui_.set_matrix_val(lon_row, 1, "_x:");
01120             }
01121             else if(lon_row == row)
01122             {
01123                 val = "_x:";
01124                 CiCommander::gui_.set_matrix_val(lat_row, 1, "_y:");
01125             }    
01126         }
01127         
01128     }
01129     else if(input == 'm')
01130     {
01131         int id = insert_modem_id();
01132         val = as<string>(id);
01133     }
01134 }
01135 
01136 void CommandGui::find_lat_lon_rows(int row, int & lat_row, int & lon_row)
01137 {
01138     lat_row = 0;
01139     lon_row = 0;
01140 
01141     int row_search = row;
01142     int row_other = 0;
01143     // rows start at 1
01144     int max_row = rowtitle_.size()+1;
01145     string current_row = boost::to_lower_copy(rowtitle_[row-1]);
01146         
01147     string::size_type lat_pos = current_row.find("lat");
01148     string::size_type lon_pos = current_row.find("lon");
01149 
01150     if(lon_pos != string::npos)
01151     {
01152         lon_row = row;
01153         int n = 1;
01154         bool end_hit = false;
01155         while(!end_hit)
01156         {
01157             string::size_type lat_pos_plus = string::npos, lat_pos_minus = string::npos;
01158             
01159             if((row_search + n) < max_row)
01160             {
01161                 lat_pos_plus = boost::to_lower_copy(rowtitle_[row_search+1-1]).find("lat");
01162                 end_hit = true;
01163             }
01164             
01165             if((row_search - n) > 0)
01166             {
01167                 lat_pos_minus = boost::to_lower_copy(rowtitle_[row_search-1-1]).find("lat");
01168                 end_hit = true;
01169             }
01170 
01171             if(lat_pos_plus == string::npos && lat_pos_minus == string::npos)
01172             {
01173                 // cannot find the row
01174                 return;
01175             }
01176             else if(lat_pos_plus != string::npos && lat_pos_minus == string::npos)
01177             {
01178                 //plus found it
01179                 row_other = row_search+1;
01180                 break;
01181             }
01182             else if(lat_pos_plus == string::npos && lat_pos_minus != string::npos)
01183             {
01184                 //minus found it
01185                 row_other = row_search-1;
01186                 break;
01187             }
01188             else if(lat_pos_plus != string::npos && lat_pos_minus != string::npos)
01189             {
01190                 // both found it, just default to plus
01191                 row_other = row_search+1;
01192                 break;
01193             }
01194 
01195             
01196             ++n;
01197         }
01198 
01199         lat_row = row_other;        
01200     }
01201     else if(lat_pos != string::npos)
01202     {
01203         lat_row = row; 
01204         int n = 1;
01205         bool end_hit = false;
01206         while(!end_hit)
01207         {
01208             string::size_type lon_pos_plus = string::npos, lon_pos_minus = string::npos;
01209             if((row_search + n) < max_row)
01210             {
01211                 lon_pos_plus = boost::to_lower_copy(rowtitle_[row_search+1-1]).find("lon");
01212                 end_hit = true;
01213             }
01214                 
01215             if((row_search - n) > 0)
01216             {
01217                 lon_pos_minus = boost::to_lower_copy(rowtitle_[row_search-1-1]).find("lon");
01218                 end_hit = true;
01219             }
01220 
01221             if(lon_pos_plus == string::npos && lon_pos_minus == string::npos)
01222             {
01223                 // cannot find the row
01224                 return;
01225             }
01226             else if(lon_pos_plus != string::npos && lon_pos_minus == string::npos)
01227             {
01228                 //plus found it
01229                 row_other = row_search+1;
01230                 break;
01231             }
01232             else if(lon_pos_plus == string::npos && lon_pos_minus != string::npos)
01233             {
01234                 //minus found it
01235                 row_other = row_search-1;
01236                 break;
01237             }
01238             else if(lon_pos_plus != string::npos && lon_pos_minus != string::npos)
01239             {
01240                 //both found it, default to minus
01241                 row_other = row_search-1;
01242                 break;
01243             }
01244                 
01245             ++n;
01246         }
01247         lon_row = row_other; 
01248     }
01249 }
01250 
01251 
01252 vector< boost::shared_ptr<DCCLMessageVar> > CommandGui::fetch_message_vars(DCCLMessage* msg)
01253 {
01254     vector< boost::shared_ptr<DCCLMessageVar> > message_vars;
01255 
01256     // set src_id and dest_id, all the rest should be automatic
01257     if(msg->header()[goby::acomms::HEAD_SRC_ID]->name() != goby::acomms::DCCL_HEADER_NAMES[goby::acomms::HEAD_SRC_ID])
01258         message_vars.push_back(msg->header()[goby::acomms::HEAD_SRC_ID]);
01259 
01260     if(msg->header()[goby::acomms::HEAD_DEST_ID]->name() != goby::acomms::DCCL_HEADER_NAMES[goby::acomms::HEAD_DEST_ID])
01261         message_vars.push_back(msg->header()[goby::acomms::HEAD_DEST_ID]);
01262     
01263     message_vars.insert(message_vars.end(),
01264                         msg->layout().begin(),
01265                         msg->layout().end());
01266 
01267     return message_vars;
01268 }
01269 
01270 void CommandGui::initialize()
01271 {
01272     CiCommander::gui_.initialize();
01273     
01274     for(int i = 0, n = CiCommander::cfg_.load_size(); i < n; ++i)
01275         do_open(CiCommander::cfg_.load(i));
01276     
01277     initialized_ = true;
01278 }    
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends