servers/usarsim/main.cc

Go to the documentation of this file.
00001 /*
00002  * This file is part of openSDK.
00003  *
00004  * Copyright (C) 2007 openSDK team
00005  *
00006  * openSDK is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; version 2.
00009  *
00010  * openSDK is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with openSDK; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  *
00019  *     $Id: main.cc,v 1.27 2007/08/13 11:19:31 nuno-lopes Exp $
00020  */
00021 
00022 #include <iostream>
00023 #include <iomanip>
00024 #include <cstring>
00025 #include <cstdlib>
00026 #include <cstdio>
00027 #include <cstdarg>
00028 #include <unistd.h>
00029 #include <netdb.h>
00030 #include <limits.h>
00031 #include <sys/time.h>
00032 #include <sys/types.h>
00033 #include <sys/socket.h>
00034 #include <sys/un.h>
00035 #include <arpa/inet.h>
00036 #include <netinet/tcp.h>
00037 #include <pthread.h>
00038 #include <signal.h>
00039 #include "config.h"
00040 #include "images.h"
00041 #include <Platform.h>
00042 #define USARSIM
00043 #include <Primitives.h>
00044 #include <OPENR/ODataFormats.h>
00045 
00046 #define CHECK_SYSCALL(val, msg) if ((val) < 0) { perror(msg); exit(4); }
00047 
00048 using namespace std;
00049 
00050 enum team_type {
00051         NO_TEAM,
00052         TEAM_BLUE,
00053         TEAM_RED
00054 };
00055 
00056 static team_type team                    = DEFAULT_USARSIM_ROBOT_TEAM;
00057 static const char *usarsim_host          = DEFAULT_USARSIM_HOST;
00058 static short usarsim_port                = DEFAULT_USARSIM_PORT;
00059 static short usarsim_imgserver_port      = DEFAULT_USARSIM_IMGSERVER_PORT;
00060 static const char *usarsim_robot         = DEFAULT_USARSIM_ROBOT;
00061 static const char *usarsim_location      = DEFAULT_USARSIM_LOCATION;
00062 static const char *usarsim_ball_location = DEFAULT_USARSIM_BALL_LOCATION;
00063 static bool display_fps                  = DEFAULT_DISPLAY_IMAGE_FPS;
00064 
00065 static int usarsim_socket;
00066 static int usarsim_ball_socket;
00067 static FILE *usarsim_imgserver_socket;
00068 static int actuators_socket;
00069 static FILE *sensors_socket;
00070 
00071 static double joint_values[num_of_primitives] = {INT_MIN};
00072 
00073 
00075 static const char* team_str(void)
00076 {
00077         switch (team) {
00078                 case NO_TEAM:   return "NO_TEAM";
00079                 case TEAM_BLUE: return "BLUE";
00080                 case TEAM_RED:  return "RED";
00081         }
00082 
00083         return NULL; // keep gcc happy..
00084 }
00085 
00086 
00088 static bool connect_to_usarsim(int *sock, int port = usarsim_port, bool read_init_msg = true)
00089 {
00090         char buffer[BUFFER_SIZE];
00091         struct sockaddr_in addr;
00092         struct hostent *h = gethostbyname(usarsim_host);
00093 
00094         if (!h) {
00095                 cerr << "Could not resolve the USARSim host: '" << usarsim_host << "'" << endl;
00096                 return false;
00097         }
00098 
00099         memset(&addr, 0, sizeof(struct sockaddr_in));
00100         memcpy(&addr.sin_addr, h->h_addr, h->h_length);
00101         addr.sin_family = AF_INET;
00102         addr.sin_port = htons(port);
00103 
00104         *sock = socket(AF_INET, SOCK_STREAM, 0);
00105         if (*sock < 0) {
00106                 perror("couldn't create a new socket");
00107                 return false;
00108         }
00109 
00110         if (connect(*sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
00111                 perror("error connecting to USARSim server");
00112                 close(*sock);
00113                 return false;
00114         }
00115 
00116         // read the initial message from the server
00117         if (read_init_msg) {
00118                 ssize_t ret = read(*sock, buffer, sizeof(buffer));
00119                 if (ret <= 0) {
00120                         cerr << "error reading the initial message from the USARSim server" << endl;
00121                         close(*sock);
00122                         return false;
00123                 }
00124 
00125                 fwrite(buffer, ret, 1, stdout);
00126         }
00127 
00128 #if defined(TCP_NODELAY) && defined(IPPROTO_TCP)
00129         // disable buffering of joint actions (creates more packets, but improves sense of realism)
00130         const int opt = 1;
00131         if (setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
00132                 perror("could not set TCP_NODELAY");
00133         }
00134 #endif
00135 
00136         return true;
00137 }
00138 
00139 
00141 static int connect_to_opensdk_sensor(const char *MSdir, const char *socket_name)
00142 {
00143         char filename[PATH_MAX];
00144         snprintf(filename, sizeof(filename), "%s/%s", MSdir, socket_name);
00145 
00146         struct sockaddr_un addr;
00147         addr.sun_family = AF_UNIX;
00148         strcpy(addr.sun_path, filename);
00149 
00150         int s = socket(AF_UNIX, SOCK_STREAM, 0);
00151 
00152         for (int i=0; i < 1000; ++i) {
00153                 if (connect(s, (const sockaddr*)&addr, sizeof(addr)) < 0) {
00154                         opensdk_yield();
00155                         continue;
00156                 }
00157                 goto ok;
00158         }
00159 
00160         perror("Couldn't connect to the openSDK sensor server socket");
00161 
00162 ok:
00163         return s;
00164 }
00165 
00166 
00168 static int connect_to_opensdk_actuator(const char *MSdir)
00169 {
00170         char filename[PATH_MAX];
00171         snprintf(filename, sizeof(filename), "%s/" ACTUATORS_SERVER_SOCKET, MSdir);
00172 
00173         struct sockaddr_un addr;
00174         socklen_t sizeof_addr = sizeof(addr);
00175         addr.sun_family = AF_UNIX;
00176         strcpy(addr.sun_path, filename);
00177 
00178         int s = socket(AF_UNIX, SOCK_STREAM, 0);
00179         int sock;
00180 
00181         unlink(filename);
00182         CHECK_SYSCALL(bind(s, (const struct sockaddr*)&addr, sizeof(addr)), "bind() failed")
00183         CHECK_SYSCALL(listen(s, 1), "listen() failed")
00184         CHECK_SYSCALL(sock = accept(s, (struct sockaddr*)&addr, &sizeof_addr), "accept() failed")
00185 
00186         close(s);
00187         return sock;
00188 }
00189 
00190 
00192 static void send_usarsim(int sock, const char *format, ...) PRINTF_FORMAT;
00193 static void send_usarsim(int sock, const char *format, ...)
00194 {
00195         va_list ap;
00196         char buffer[BUFFER_SIZE];
00197         int bufsize;
00198 
00199         va_start(ap, format);
00200         bufsize = vsprintf(buffer, format, ap);
00201         va_end(ap);
00202 
00203         if (DEBUG_USARSIM_PROTOCOL) {
00204                 cout << "[->USARSim] " << buffer;
00205         }
00206 
00207         if (write(sock, buffer, bufsize) < 0) {
00208                 perror("error writing to the USARSim socket");
00209                 exit(0x31);
00210         }
00211 }
00212 
00213 
00215 static void* read_usarsim_messages(void*)
00216 {
00217         char buffer[BUFFER_SIZE];
00218         FILE *fp = fdopen(usarsim_socket, "r");
00219 
00220         while (true) {
00221                 if (!fgets(buffer, sizeof(buffer), fp)) {
00222                         cerr << "error reading from USARSim socket" << endl;
00223                         exit(0xdd);
00224                 }
00225 
00226                 if (!strncmp(buffer, "STA ", sizeof("STA ")-1)) {
00227                         // state and mission Package messages
00228                         // these messages aren't useful for us, so just skip them (5 per second)
00229                         continue;
00230                 }
00231 
00232                 if (DEBUG_USARSIM_PROTOCOL) {
00233                         buffer[strlen(buffer)-1] = '\0'; // remove newline char
00234                         cout << "[<-USARSim] " << buffer << endl;
00235                 }
00236 
00237                 if (!strncmp(buffer, "SEN ", sizeof("SEN ")-1)) {
00238                         // Sensor readings
00239                         char *msg = buffer + sizeof("SEN ")-1;
00240 
00241                         if (!strncmp("{Type IR} ", msg, sizeof("{Type IR} ")-1)) {
00242                                 msg += sizeof("{Type IR} ")-1;
00243                                 double IRN, IRF, EDG; // near, far and edge IR sensors
00244                                 if (sscanf(msg, "{Name IRN Range %lf} {Name IRF Range %lf} {Name EDG Range %lf}", &IRN, &IRF, &EDG) != 3) {
00245                                         goto sensors_error;
00246                                 }
00247 
00248                                 fprintf(sensors_socket, "IRN:%lf\nIRF:%lf\nEDG:%lf\n", IRN, IRF, EDG);
00249                                 fflush(sensors_socket);
00250 
00251                         } else if (!strncmp("{Type Accel} ", msg, sizeof("{Type Accel} ")-1)) {
00252                                 msg += sizeof("{Type Accel} ")-1;
00253                                 double ax, ay, az;
00254                                 if (sscanf(msg, "{Name ACC} {Acceleration %lf,%lf,%lf}", &ax, &ay, &az) != 3) {
00255                                         goto sensors_error;
00256                                 }
00257 
00258                                 fprintf(sensors_socket, "AX:%lf\nAY:%lf\nAZ:%lf\n", ax, ay, az);
00259                                 fflush(sensors_socket);
00260 
00261                         } else if (!strncmp("{Type Helper}", msg, sizeof("{Type Helper}")-1)) {
00262                                 continue; // this message has no interest for us
00263 
00264                         } else {
00265 sensors_error:
00266                                 cerr << "bogus sensor readings message received from USARSim: '" << msg << "'" << endl;
00267                         }
00268 
00269                 } else {
00270                         cerr << "bogus message received: '" << buffer << "'" << endl;
00271                 }
00272         }
00273 
00274         return NULL;
00275 }
00276 
00277 
00279 static void* read_usarsim_images(void *MSdir)
00280 {
00281         // connect to the image server
00282         int img_socket = connect_to_opensdk_sensor((const char*)MSdir, IMAGE_SERVER_SOCKET);
00283         FILE *opensdk_socket = fdopen(img_socket, "w");
00284         if (!opensdk_socket) {
00285                 exit(0x08);
00286         }
00287 
00288         if (connect_to_usarsim(&img_socket, usarsim_imgserver_port, false)) {
00289                 usarsim_imgserver_socket = fdopen(img_socket, "r+");
00290                 if (!usarsim_imgserver_socket) {
00291                         exit(0x43);
00292                 }
00293 
00294         } else {
00295                 cout << "Not connected to USARSim image server" << endl;
00296                 return NULL;
00297         }
00298 
00299 
00300         unsigned char buffer[IMG_SIZE], newimg[IMG_SIZE];
00301 
00302         byte *Y  = newimg;
00303         byte *Cb = newimg+(IMG_SIZE*2/3);
00304         byte *Cr = newimg+IMG_SIZE/3;
00305 
00306         struct timeval last_tv = {0};
00307         unsigned int num_of_images = 0;
00308 
00309         while (true) {
00310                 if (fread(buffer, 1, 1, usarsim_imgserver_socket) != 1) {
00311                         cerr << "error reading image type from USARSim" << endl;
00312                         return NULL;
00313                 }
00314 
00315                 if (*buffer != 0) { // check the image type. for now accept only RAW images
00316                         cerr << "Invalid image type received: " << (char)(*buffer + '0') << endl;
00317                         return NULL;
00318                 }
00319 
00320                 uint32_t size;
00321                 uint16_t width, height;
00322 
00323                 // we do several calls because of memory alignment issues on some platforms
00324                 if (!fread(&size, 4, 1, usarsim_imgserver_socket) || !fread(&width, 2, 1, usarsim_imgserver_socket) || !fread(&height, 2, 1, usarsim_imgserver_socket)) {
00325                         cerr << "error reading image info from USARSim" << endl;
00326                 }
00327 
00328                 size   = ntohl(size);
00329                 width  = ntohs(width);
00330                 height = ntohs(height);
00331 
00332                 if (width != IMG_WIDTH || height != IMG_HEIGHT) {
00333                         cerr << "the image received doesn't match the required dimensions (" << IMG_WIDTH << 'x' << IMG_HEIGHT <<
00334                         "). received: " << width << 'x' << height << endl;
00335                         return NULL;
00336                 }
00337 
00338                 if (size-4 != IMG_SIZE) { // 4 bytes were already fetched (the dimensions)
00339                         cerr << "the image received doesn't match the required size (" << IMG_SIZE << "). received: " << size << endl;
00340                         return NULL;
00341                 }
00342 
00343                 if (!fread(buffer, IMG_SIZE, 1, usarsim_imgserver_socket)) {
00344                         cerr << "error reading data from USARSim image server" << endl;
00345                         return NULL;
00346                 }
00347 
00348 
00349                 if (display_fps) {
00350                         struct timeval tv;
00351                         gettimeofday(&tv, NULL);
00352 
00353                         ++num_of_images;
00354 
00355                         if (tv.tv_sec != last_tv.tv_sec) {
00356                                 float seconds = (tv.tv_sec + tv.tv_usec / 1000000.0) - (last_tv.tv_sec + last_tv.tv_usec / 1000000.0);
00357                                 float fps = num_of_images / seconds;
00358                                 cout << "\r" << fixed << setprecision(2) << fps << " fps" << flush;
00359                                 num_of_images = 0;
00360                                 last_tv = tv;
00361                         }
00362                 }
00363 
00364 
00365                 RGB_to_YCbCr(buffer, IMG_WIDTH, IMG_HEIGHT, Y, Cb, Cr);
00366 
00367                 if (!fwrite(newimg, IMG_SIZE, 1, opensdk_socket)) {
00368                         cerr << "error writing to openSDK image socket" << endl;
00369                         return NULL;
00370                 }
00371 
00372                 shrink_frame(IMG_WIDTH, IMG_HEIGHT, 2, Y, Cb, Cr, buffer, buffer+(IMG_SIZE/4*2/3), buffer+(IMG_SIZE/4/3));
00373 
00374                 if (!fwrite(buffer, IMG_SIZE/4, 1, opensdk_socket)) {
00375                         cerr << "error writing to openSDK image socket" << endl;
00376                         return NULL;
00377                 }
00378 
00379                 shrink_frame(IMG_WIDTH, IMG_HEIGHT, 4, Y, Cb, Cr, buffer, buffer+(IMG_SIZE/16*2/3), buffer+(IMG_SIZE/16/3));
00380 
00381                 if (!fwrite(buffer, IMG_SIZE/16, 1, opensdk_socket)) {
00382                         cerr << "error writing to openSDK image socket" << endl;
00383                         return NULL;
00384                 }
00385 
00386 /*FILE *fp = fopen("sample-aibo.raw", "w");
00387                 if (!fwrite(buffer, IMG_SIZE, 1, fp)) {
00388                         cerr << "error writing to openSDK image socket" << endl;
00389                         return NULL;
00390                 }
00391 exit(0);*/
00392 
00393                 // ask for another image
00394                 fwrite("OK", 2, 1, usarsim_imgserver_socket);
00395         }
00396 
00397         return NULL;
00398 }
00399 
00400 
00402 static void read_opensdk_messages(void)
00403 {
00404         char buffer[BUFFER_SIZE];
00405         FILE *fp = fdopen(actuators_socket, "r");
00406 
00407         while (true) {
00408                 if (!fgets(buffer, sizeof(buffer), fp)) {
00409                         cerr << "error reading from openSDK socket" << endl;
00410                         exit(0x23);
00411                 }
00412 
00413                 char *str = buffer;
00414                 long primitiveId = strtol(str, &str, 10);
00415                 double *table = &joint_values[primitiveId];
00416 
00417                 do {
00418                         double val = strtod(str, &str);
00419                         *table = val;
00420                 } while (*str != '\n' && *str);
00421         }
00422 }
00423 
00424 
00425 static void sigalrm_handler(int)
00426 {
00427         char buffer[BUFFER_SIZE], *str = buffer + sizeof("MULTIDRIVE")-1;
00428         char buffer_opensdk[BUFFER_SIZE], *str_opensdk = buffer_opensdk;
00429         *str = '\0';
00430 
00431         for (unsigned int i=0; i < num_of_primitives; ++i) {
00432                 double val = joint_values[i];
00433                 if (val != INT_MIN) {
00434                         if (usarsim_primitives[i]) { // ignore primitives that USARSim doesn't support
00435                                 str += sprintf(str, " {%s %lf}", usarsim_primitives[i], val);
00436                                 str_opensdk += sprintf(str_opensdk, "J%d:%lf\n", i, val);
00437                         }
00438                         joint_values[i] = INT_MIN; // reset the value
00439                 }
00440         }
00441 
00442         if (*(buffer + sizeof("MULTIDRIVE")-1)) {
00443                 memcpy(buffer, "MULTIDRIVE", sizeof("MULTIDRIVE")-1);
00444                 sprintf(str, "\r\n");
00445                 send_usarsim(usarsim_socket, buffer);
00446                 fwrite(buffer_opensdk, strlen(buffer_opensdk), 1, sensors_socket);
00447                 fflush(sensors_socket);
00448         }
00449 }
00450 
00451 
00452 int main(int argc, char *argv[])
00453 {
00454         const char *MSdir = NULL;
00455 
00456         while (--argc) {
00457                 const char *arg = *++argv;
00458 
00459                 if (!strncmp(arg, "--team=", sizeof("--team=")-1)) {
00460                         arg += sizeof("--team=")-1;
00461 
00462                         if (!strcasecmp(arg, "blue")) {
00463                                 team = TEAM_BLUE;
00464                         } else if (!strcasecmp(arg, "red")) {
00465                                 team = TEAM_RED;
00466                         } else if (!strcasecmp(arg, "no_team")) {
00467                                 team = NO_TEAM;
00468                         } else {
00469                                 cerr << "Warning: team '" << arg << "' not supported!" << endl;
00470                         }
00471 
00472                 } else if (!strncmp(arg, "--usarsim-host=", sizeof("--usarsim-host=")-1)) {
00473                         usarsim_host = arg + sizeof("--usarsim-host=")-1;
00474 
00475                 } else if (!strncmp(arg, "--usarsim-port=", sizeof("--usarsim-port=")-1)) {
00476                         usarsim_port = atoi(arg + sizeof("--usarsim-port=")-1);
00477 
00478                 } else if (!strncmp(arg, "--usarsim-robot=", sizeof("--usarsim-robot=")-1)) {
00479                         usarsim_robot = arg + sizeof("--usarsim-robot=")-1;
00480 
00481                 } else if (!strncmp(arg, "--usarsim-location=", sizeof("--usarsim-location=")-1)) {
00482                         usarsim_location = arg + sizeof("--usarsim-location=")-1;
00483 
00484                 } else if (!strncmp(arg, "--usarsim-ball=", sizeof("--usarsim-ball=")-1)) {
00485                         usarsim_ball_location = arg + sizeof("--usarsim-ball=")-1;
00486 
00487                 } else if (!strncmp(arg, "--display-fps=", sizeof("--display-fps=")-1)) {
00488                         display_fps = !strncmp(arg + sizeof("--display-fps=")-1, "yes", sizeof("yes")-1);
00489 
00490                 } else if (!strcmp(arg, "--help") || !strcmp(arg, "-h")) {
00491                         cout << "./USARSimServer [options] <MS dir>" << endl << endl
00492                         << "Available options:" << endl
00493                         << "--team=TEAM\t\t\tTeam of the robot (either BLUE, RED or NO_TEAM). defaults to '" << team_str() << "'" << endl
00494                         << "--usarsim-host=HOST\t\tHost of the USARSim server. defaults to '" << DEFAULT_USARSIM_HOST << "'" << endl
00495                         << "--usarsim-port=PORT\t\tPort of the USARSim server. defaults to " << DEFAULT_USARSIM_PORT << endl
00496                         << "--usarsim-robot=MODEL\t\tUSARSim Robot Model. defaults to '" << DEFAULT_USARSIM_ROBOT << "'" << endl
00497                         << "--usarsim-location=X,Y,Z\tCoordinates of the start position. defaults to '" << DEFAULT_USARSIM_LOCATION << "'" << endl
00498                         << "--usarsim-ball=[no|location]\tIf and where to place the ball. defaults to '" << DEFAULT_USARSIM_BALL_LOCATION << "'" << endl
00499                         << "--display-fps=[no|yes]\t\tDisplay image FPS received from the image server. defaults to '" << (DEFAULT_DISPLAY_IMAGE_FPS ? "yes" : "no") << "'" << endl
00500                         << endl;
00501                         return 0;
00502 
00503                 } else if (argc == 1) { //MS dir
00504                         MSdir = arg;
00505 
00506                 } else {
00507                         cerr << "option not recognised: '" << arg << "'" << endl;
00508                 }
00509         }
00510 
00511 
00512         if (!MSdir) {
00513                 cerr << "MS directory not set. exiting.." << endl;
00514                 return 0x02;
00515         }
00516 
00517         cout << "OpenSDK USARSim connector loading..." << endl;
00518 
00519         actuators_socket = connect_to_opensdk_actuator(MSdir);
00520         int sensors_fd   = connect_to_opensdk_sensor(MSdir, SENSORS_SERVER_SOCKET);
00521 
00522         if (!connect_to_usarsim(&usarsim_socket)) {
00523                 return 0x01;
00524         }
00525 
00526         sensors_socket = fdopen(sensors_fd, "w");
00527         if (!sensors_socket) {
00528                 return 0x04;
00529         }
00530 
00531         // create a ball only if asked
00532         if (strcasecmp(usarsim_ball_location, "no")) {
00533                 if (!connect_to_usarsim(&usarsim_ball_socket)) {
00534                         return 0x02;
00535                 }
00536 
00537                 send_usarsim(usarsim_ball_socket, "INIT {ClassName USARBot.SoccerBall} {Name Ball} {Location %s}\r\n", usarsim_ball_location);
00538         }
00539 
00540         {
00541         char skin[64];
00542         if (team == NO_TEAM) {
00543                 *skin = '\0';
00544         } else {
00545                 sprintf(skin, "{Skin %s} ", team_str());
00546         }
00547 
00548         // spawn a new robot
00549         send_usarsim(usarsim_socket, "INIT {ClassName USARBot.%s} %s{Location %s}\r\n", usarsim_robot, skin, usarsim_location);
00550         }
00551 
00552         // create a thread to receive data from USARSim
00553         pthread_t thread;
00554         pthread_create(&thread, NULL, read_usarsim_messages, NULL);
00555 
00556         {
00557         struct itimerval timer = {
00558                 {0, 10}, {0, 10}
00559         };
00560         signal(SIGALRM, sigalrm_handler);
00561 
00562         if (setitimer(ITIMER_REAL, &timer, NULL) < 0) {
00563                 perror("setitimer() failed");
00564                 return 0x41;
00565         }
00566         }
00567 
00568         pthread_create(&thread, NULL, read_usarsim_images, (void*)MSdir);
00569 
00570 
00571         // alloc the main thread to the other task
00572         read_opensdk_messages();
00573 
00574         return 0;
00575 }

Generated on Sun Dec 2 23:04:29 2007 for openSDK by  doxygen 1.3.9.1