00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <iostream>
00029 #include <sstream>
00030 #include "iwakishi.h"
00031 #include <algorithm>
00032 #include <string>
00033
00034 #include <locale.h>
00035 #include <ncursesw/ncurses.h>
00036
00037 #define KEY_SMALL_Q 113
00038 #define KEY_SMALL_V 118
00039 #define KEY_CONTROL_SMALL_Q 17
00040 #define KEY_CONTROL_SMALL_V 22
00041 #define MAX_NODE_WIDTH 20
00042 #define ROOT_NODE_WIDTH 4
00043
00044
00045 UICommand TextUI::update(InteractionManager &im, int ch) {
00046 UICommand ui_command = uiNone;
00047 int nrow, ncol, msg_height, tree_height, globals_height = 1, header_height = 2;
00048 int keyboard_buffer_height = 1;
00049
00050
00051
00052
00053
00054 getmaxyx(stdscr, nrow, ncol);
00055
00056 if (nrow <= this->msgPaneHeight + header_height + this->minTreeHeight) {
00057 msg_height = 0;
00058 } else {
00059 msg_height = this->msgPaneHeight;
00060 }
00061 tree_height = nrow - header_height - msg_height - globals_height -
00062 keyboard_buffer_height;
00063
00064
00065
00066
00067
00068 clear();
00069
00070 move(0,0);
00071 this->printHeader();
00072
00073 move(header_height, 0);
00074 this->printPlanTree(im.ptree, tree_height);
00075
00076 move(header_height + tree_height, 0);
00077 this->printGlobals( *im.getGlobalBindings() );
00078
00079 move(header_height + tree_height + globals_height, 0);
00080 this->printMessages(msg_height);
00081
00082 move(header_height + tree_height + globals_height + msg_height, 0);
00083 this->printKeyboardBuffer();
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093 if ( ch == KEY_CONTROL_SMALL_Q ) {
00094 printw("Goodbye");
00095 ui_command = uiQuit;
00096 } else if ( ch == KEY_CONTROL_SMALL_V ) {
00097 move(1,0);
00098 if (this->verbosity == WithBodyElements) {
00099 printw("brief");
00100 this->verbosity = Brief;
00101 } else if (this->verbosity == Brief) {
00102 printw("verbose");
00103 this->verbosity = Verbose;
00104 } else {
00105 printw("with body elements");
00106 this->verbosity = WithBodyElements;
00107 }
00108 }
00109
00110
00111 refresh();
00112
00113
00114 if (this->tick_id >= MAX_UI_TICK_ID) {
00115 this->tick_id = 0;
00116 } else {
00117 this->tick_id++;
00118 }
00119
00120 return ui_command;
00121 }
00122
00123 void TextUI::init() {
00124 setlocale(LC_CTYPE,"");
00125 initscr();
00126 raw();
00127 keypad(stdscr, TRUE);
00128 noecho();
00129 timeout(0);
00130 }
00131
00132
00133 void TextUI::close() {
00134 endwin();
00135 }
00136
00137
00138 void TextUI::printPlanTree(PlanTree &ptree, const int &tree_height) {
00139 int y = 0, x = 0, dy = 0, depth_old = 0, depth = 0, dx = 0;
00140
00141 getyx(stdscr, y, x);
00142
00143 if (this->verbosity == Brief) {
00144 tree<Node>::iterator sib2=ptree.tr.begin();
00145 tree<Node>::iterator end2=ptree.tr.end();
00146 while (( sib2!=end2 ) && (dy < tree_height)) {
00147 dx = ptree.tr.depth(sib2);
00148 move(y + dy, x + dx);
00149 printw( (*sib2).recipe_name.c_str() );
00150 ++sib2;
00151 ++dy;
00152 }
00153 } else if (this->verbosity == Verbose) {
00154
00155
00156
00157 tree<Node>::iterator sib2=ptree.tr.begin();
00158 tree<Node>::iterator end2=ptree.tr.end();
00159 while (( sib2 != end2 ) && (dy < tree_height)) {
00160 dx = ptree.tr.depth(sib2);
00161 move(y + dy, x + dx);
00162 printw( (*sib2).recipe_name.c_str() );
00163
00164
00165 if (sib2->recipe_name != "ROOT") {
00166 string recipe_status = " " + to_string(sib2->node_id) + " " +
00167 to_string(sib2->active_element) + " " +
00168 to_string(sib2->body.elements.size());
00169 printw(recipe_status.c_str());
00170 }
00171 ++sib2;
00172 ++dy;
00173 }
00174 } else {
00175 tree<Node>::iterator sib2_old;
00176 tree<Node>::iterator sib2=ptree.tr.begin();
00177 tree<Node>::iterator end2=ptree.tr.end();
00178 while (( sib2 != end2 ) && (dy < tree_height)) {
00179 depth_old = depth;
00180 depth = ptree.tr.depth(sib2);
00181 dx = ROOT_NODE_WIDTH + (depth-1) * MAX_NODE_WIDTH;
00182
00183 if (sib2->recipe_name == "ROOT") {
00184 dy = 0;
00185 dx = 0;
00186 } else if (depth > depth_old) {
00187
00188
00189 dy += 1 + ((sib2_old->recipe_name == "ROOT") ? 0 : sib2_old->active_element);
00190 } else {
00191
00192
00193
00194 dy += 1 + sib2_old->body.elements.size();
00195
00196 }
00197
00198 move(y + dy, x + dx);
00199
00200 if (sib2->recipe_name == "ROOT") {
00201 string root_name = "ROOT";
00202 printw( root_name.c_str() );
00203 } else {
00204 string recipe_status = "-" + sib2->recipe_name + ":" + to_string(sib2->node_id) + " " +
00205 to_string(sib2->active_element) + " " + to_string(sib2->body.elements.size());
00206 printw(recipe_status.c_str());
00207
00208 if (sib2->bodyElementDescriptionsHaveBeenSet) {
00209 this->updateBodyElementDescriptions(*sib2);
00210 } else {
00211 this->setBodyElementDescriptions(*sib2);
00212 }
00213
00214 for (int i = 0; (unsigned int)i < sib2->body.elements.size(); i++) {
00215 string body_element_blurp = ((i == sib2->active_element) ? "*":" ") +
00216 to_string(i) + "." + sib2->element_descriptions[i];
00217 move(y + dy + i + 1, x + dx);
00218 printw(body_element_blurp.c_str());
00219 }
00220 }
00221 sib2_old = sib2;
00222 ++sib2;
00223 }
00224 }
00225 }
00226
00227 void TextUI::printHeader() {
00228 string header = "[ctrl-v]erbose/brief [ctrl-q]uit [ctrl-h]elp";
00229 printw( header.c_str() );
00230 }
00231
00232 void TextUI::printMessages(const int &msg_height) {
00233 int y, x, dy = msg_height - 1, dx = 0;
00234 getyx(stdscr, y, x);
00235
00236 std::list<string>::iterator msg_it = this->messages.begin();
00237
00238 while (( msg_it != this->messages.end() ) && (dy >= 0)) {
00239
00240 move(y + dy, x + dx);
00241 printw( msg_it->c_str() );
00242 ++msg_it;
00243 --dy;
00244 }
00245 }
00246
00247 void TextUI::printKeyboardBuffer() {
00248 printw( this->keyboardBuffer.c_str() );
00249 }
00250
00251
00252 void TextUI::push_msg(const string &msg) {
00253 if (this->messages.size() >= MAX_UI_MSGS) {
00254 this->messages.pop_back();
00255 }
00256 this->messages.push_front(NowTime() + " " + msg);
00257 }
00258 string TextUI::makeActionDescription(BodyElement &element, int active_element, int element_id) {
00259 if (element_id <= active_element) {
00260 return makePastActionDescription(element);
00261 } else {
00262 return makeFutureActionDescription(element);
00263 }
00264
00265 }
00266
00267 string TextUI::makeFutureActionDescription(BodyElement &element) {
00268 string result;
00269 int rand_element;
00270 std::ostringstream output;
00271
00272 if (element.element_type != "action") {
00273 return "_ERROR_";
00274 }
00275
00276 result = element.name + " ";
00277
00278 if (element.random) {
00279 result += "rnd ";
00280
00281 if (element.random_elements.size() == 0) {
00282 return "_ERROR_";
00283 }
00284
00285 rand_element = (this->tick_id) % element.random_elements.size();
00286 result = result + "p=";
00287 output << setprecision(2) << element.element_probs[rand_element];
00288 result += output.str();
00289 result += " ";
00290
00291
00292 std::list<ArgSlot>::iterator arg_it = element.random_elements[rand_element].args.begin();
00293 while (arg_it != element.random_elements[rand_element].args.end()) {
00294 if (arg_it->name == "utterance_file") {
00295 result += abbreviateString(arg_it->value, 10, 15);
00296 }
00297 arg_it++;
00298 }
00299 } else {
00300
00301 std::list<ArgSlot>::iterator arg_it = element.args.begin();
00302 while (arg_it != element.args.end()) {
00303 if (arg_it->name == "utterance_file") {
00304 result += abbreviateString(arg_it->value, 10, 15);
00305 }
00306 arg_it++;
00307 }
00308 }
00309
00310 return result;
00311
00312 }
00313
00314
00315 string TextUI::makePastActionDescription(BodyElement &element) {
00316 string result;
00317 int rand_element;
00318 std::ostringstream output;
00319
00320 if (element.element_type != "action") {
00321 return "_ERROR_";
00322 }
00323
00324 result = element.name + " ";
00325
00326 if (element.random) {
00327 result += "rnd ";
00328
00329 if (element.random_elements.size() == 0) {
00330 return "_ERROR_";
00331 }
00332
00333 rand_element = element.chosen_outcome;
00334 result = result + "p=";
00335 output << setprecision(2) << element.element_probs[rand_element];
00336 result += output.str();
00337 result += " ";
00338
00339
00340 std::list<ArgSlot>::iterator arg_it = element.random_elements[rand_element].args.begin();
00341 while (arg_it != element.random_elements[rand_element].args.end()) {
00342 if (arg_it->name == "utterance_file") {
00343 result += abbreviateString(arg_it->value, 10, 15);
00344 }
00345 arg_it++;
00346 }
00347 } else {
00348
00349 std::list<ArgSlot>::iterator arg_it = element.args.begin();
00350 while (arg_it != element.args.end()) {
00351 if (arg_it->name == "utterance_file") {
00352 result += abbreviateString(arg_it->value, 10, 15);
00353 }
00354 arg_it++;
00355 }
00356 }
00357
00358 return result;
00359
00360 }
00361
00362
00363 void TextUI::setBodyElementDescriptions(Node &aNode) {
00364
00365 aNode.element_descriptions.clear();
00366 for (int i = 0; (unsigned int)i < aNode.body.elements.size(); i++) {
00367 aNode.element_descriptions.push_back("");
00368 this->updateBodyElementDescription(aNode, i);
00369 }
00370 aNode.bodyElementDescriptionsHaveBeenSet = true;
00371 }
00372
00373
00374
00375
00376
00377
00378 void TextUI::updateBodyElementDescriptions(Node &aNode) {
00379
00380 for (int i = max(0, aNode.active_element); (unsigned int)i < aNode.body.elements.size(); i++) {
00381 if (aNode.body.elements[i].random) {
00382 this->updateBodyElementDescription(aNode, i);
00383 }
00384 }
00385 }
00386
00387
00388 void TextUI::updateBodyElementDescription(Node &aNode, int element_id) {
00389
00390 if ( ( (unsigned int)element_id >= aNode.body.elements.size() ) ||
00391 ( element_id < 0 ) ) {
00392 return;
00393 }
00394
00395
00396 if (aNode.body.elements[element_id].element_type == "action") {
00397
00398 aNode.element_descriptions[element_id] = "action " +
00399 makeActionDescription(aNode.body.elements[element_id], aNode.active_element, element_id);
00400 } else if (aNode.body.elements[element_id].element_type == "goal") {
00401 aNode.element_descriptions[element_id] = "goal";
00402
00403 } else if (aNode.body.elements[element_id].element_type == "assignment") {
00404 aNode.element_descriptions[element_id] = "assignment";
00405
00406 } else {
00407 aNode.element_descriptions[element_id] = "Unknown type: " +
00408 aNode.body.elements[element_id].element_type;
00409 return;
00410 }
00411 }
00412
00413
00414
00415
00416
00417
00418 std::map<string, int> TextUI::getAtomSubtypeCounts(Conjunction &gBindings) {
00419
00420 std::map<string, int> counts_map;
00421
00422 for (std::vector< Atom >::iterator atom_it=gBindings.atoms.begin();
00423 atom_it != gBindings.atoms.end(); atom_it++) {
00424 if (counts_map.count(atom_it->readSlotVal("subtype")) == 0) {
00425 counts_map[atom_it->readSlotVal("subtype")] = 1;
00426 } else {
00427 counts_map[atom_it->readSlotVal("subtype")] += 1;
00428 }
00429 }
00430
00431 return counts_map;
00432
00433 }
00434
00435
00436
00437
00438 void TextUI::printGlobals(Conjunction &gBindings) {
00439 int nrow, ncol, y, x, dy = 0, dx = 0;
00440
00441 getmaxyx(stdscr, nrow, ncol);
00442 getyx(stdscr, y, x);
00443
00444 std::map<string, int> atomSubtypeCounts = this->getAtomSubtypeCounts(gBindings);
00445
00446 std::map<string, int>::iterator entry_it = atomSubtypeCounts.begin();
00447 while (( entry_it != atomSubtypeCounts.end() ) &&
00448 (dx <= ncol - (int)entry_it->first.size() - 3)) {
00449
00450 move(y + dy, x + dx);
00451 string count_string = entry_it->first + ":" + to_string(entry_it->second) + " ";
00452 printw( count_string.c_str() );
00453 ++entry_it;
00454 dx += count_string.size();
00455 }
00456 }