Goby Underwater Autonomy Project
Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
|
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 }