Fawkes API  Fawkes Development Version
net.cpp
00001 
00002 /***************************************************************************
00003  *  net.cpp - Generic network tool
00004  *
00005  *  Created: Fri Nov 16 10:27:57 2007
00006  *  Copyright  2005-2009  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include <fvutils/net/fuse.h>
00024 #include <fvutils/net/fuse_client.h>
00025 #include <fvutils/net/fuse_client_handler.h>
00026 #include <fvutils/net/fuse_message.h>
00027 #include <fvutils/net/fuse_image_content.h>
00028 #include <fvutils/net/fuse_lut_content.h>
00029 #include <fvutils/net/fuse_imagelist_content.h>
00030 #include <fvutils/net/fuse_lutlist_content.h>
00031 #include <fvutils/writers/fvraw.h>
00032 #include <fvutils/color/colorspaces.h>
00033 #include <fvutils/colormap/yuvcm.h>
00034 #include <fvutils/colormap/cmfile.h>
00035 
00036 #include <core/threading/mutex.h>
00037 #include <core/threading/wait_condition.h>
00038 #include <core/exceptions/software.h>
00039 #include <utils/system/argparser.h>
00040 #include <utils/system/console_colors.h>
00041 
00042 #include <netcomm/service_discovery/browse_handler.h>
00043 #ifdef HAVE_AVAHI
00044 #include <netcomm/dns-sd/avahi_thread.h>
00045 #endif
00046 
00047 // for inet_ntop
00048 #include <arpa/inet.h>
00049 #include <netinet/in.h>
00050 #include <cstring>
00051 #include <cstdlib>
00052 #include <cstdio>
00053 
00054 using namespace fawkes;
00055 using namespace firevision;
00056 
00057 /** FireVision Network Tool */
00058 class FireVisionNetworkTool
00059   : public FuseClientHandler,
00060     public ServiceBrowseHandler
00061 {
00062  public:
00063   /** Constructor.
00064    * @param argp argument parser
00065    */
00066   FireVisionNetworkTool(ArgumentParser *argp)
00067   {
00068     __argp = argp;
00069     __exploring = false;
00070     __explore_waitcond = NULL;
00071   }
00072 
00073   void
00074   fuse_invalid_server_version(uint32_t local_version,
00075                               uint32_t remote_version) throw()
00076   {
00077     printf("Invalid version received (local: %u, remote: %u)\n",
00078            local_version, remote_version);
00079   }
00080 
00081   virtual void
00082   fuse_connection_established() throw()
00083   {
00084   }
00085 
00086   virtual void
00087   fuse_connection_died() throw()
00088   {
00089   }
00090 
00091   virtual void
00092   fuse_inbound_received(FuseNetworkMessage *m) throw()
00093   {
00094     // printf("Received message of type %u\n", m->type());
00095 
00096     switch (m->type() ) {
00097     case FUSE_MT_IMAGE:
00098       // we got an image, save it to the given file
00099       try {
00100         FuseImageContent *ic = m->msgc<FuseImageContent>();
00101         if ( ic->format() == FUSE_IF_RAW ) {
00102           FvRawWriter *w = new FvRawWriter(__file, ic->pixel_width(), ic->pixel_height(),
00103                                            (colorspace_t)ic->colorspace(), ic->buffer());
00104           w->write();
00105           delete w;
00106         } else if ( ic->format() == FUSE_IF_JPEG ) {
00107           FILE *f = fopen(__file, "w");
00108           if (fwrite(ic->buffer(), ic->buffer_size(), 1, f) == 0) {
00109             printf("Failed to write data to file");
00110           }
00111           fclose(f);
00112         } else {
00113           printf("Image of unknown format (%u) received.\n", ic->format());
00114         }
00115         delete ic;
00116       } catch (Exception &e) {
00117         printf("Received message cannot be casted to FuseImageMessage\n");
00118         e.print_trace();
00119       }
00120       __client->cancel();
00121       break;
00122     case FUSE_MT_IMAGE_LIST:
00123       try {
00124         FuseImageListContent *ilc = m->msgc<FuseImageListContent>();
00125         if ( ilc->has_next() ) {
00126           printf("Available images:\n");
00127           while ( ilc->has_next() ) {
00128             FUSE_imageinfo_t *ii = ilc->next();
00129             char tmp[IMAGE_ID_MAX_LENGTH + 1];
00130             tmp[IMAGE_ID_MAX_LENGTH] = 0;
00131             strncpy(tmp, ii->image_id, IMAGE_ID_MAX_LENGTH);
00132             printf("  %s (%u x %u, %s)\n", tmp, ntohl(ii->width), ntohl(ii->height),
00133                    colorspace_to_string((colorspace_t)ntohs(ii->colorspace)));
00134           }
00135         } else {
00136           printf("No images available\n");
00137         }
00138       delete ilc;
00139       } catch (Exception &e) {
00140         printf("Received message cannot be casted to FuseImageListMessage\n");
00141         e.print_trace();
00142       }
00143       break;
00144     case FUSE_MT_LUT_LIST:
00145       try {
00146         FuseLutListContent *llc = m->msgc<FuseLutListContent>();
00147         if ( llc->has_next() ) {
00148           printf("Available lookup tables:\n");
00149           while ( llc->has_next() ) {
00150             FUSE_lutinfo_t *li = llc->next();
00151             char tmp[LUT_ID_MAX_LENGTH + 1];
00152             tmp[LUT_ID_MAX_LENGTH] = 0;
00153             strncpy(tmp, li->lut_id, LUT_ID_MAX_LENGTH);
00154             printf("  %s (%u x %u x %u, %u bpc)\n", tmp,
00155                    ntohl(li->width), ntohl(li->height),
00156                    ntohl(li->depth), ntohl(li->bytes_per_cell));
00157           }
00158         } else {
00159           printf("No lookup tables available\n");
00160         }
00161         delete llc;
00162       } catch (Exception &e) {
00163         printf("Received message cannot be casted to FuseImageListMessage\n");
00164         e.print_trace();
00165       }
00166       __client->cancel();
00167       break;
00168 
00169     case FUSE_MT_LUT:
00170       // we got a LUT, save it to the given file
00171       try {
00172         FuseLutContent *lc = m->msgc<FuseLutContent>();
00173         // Currently we expect colormaps, so make sure we get sensible dimensions
00174         if ( lc->width() != 256 ) {
00175           printf("Invalid dimensions for LUT received, colormap width %u != 256", lc->width());
00176         } else if ( lc->height() != 256 ) {
00177           printf("Invalid dimensions for LUT received, colormap height %u != 256", lc->height());
00178         } else if ( lc->depth() > 256 ) {
00179           printf("Invalid dimensions for LUT received, colormap depth %u > 256", lc->depth());
00180         } else {
00181           try {
00182             YuvColormap yuvcm(lc->depth());
00183             yuvcm.set(lc->buffer());
00184             ColormapFile cmf;
00185             cmf.add_colormap(&yuvcm);
00186             cmf.write(__file);
00187           } catch (Exception &e) {
00188             e.append("Failed to save colormap");
00189             e.print_trace();
00190           }
00191         }
00192         delete lc;
00193       } catch (Exception &e) {
00194         printf("Received message cannot be casted to FuseLutMessage\n");
00195         e.print_trace();
00196       }
00197       __client->cancel();
00198       break;
00199 
00200     case FUSE_MT_SET_LUT_SUCCEEDED:
00201       {
00202         FUSE_lutdesc_message_t *lutdesc = m->msg<FUSE_lutdesc_message_t>();
00203         char lut_id[LUT_ID_MAX_LENGTH + 1];
00204         lut_id[LUT_ID_MAX_LENGTH] = 0;
00205         strncpy(lut_id, lutdesc->lut_id, LUT_ID_MAX_LENGTH);
00206         printf("LUT %s has been uploaded successfully.\n", lut_id);
00207         __client->cancel();
00208       }
00209       break;
00210 
00211     case FUSE_MT_SET_LUT_FAILED:
00212       {
00213         FUSE_lutdesc_message_t *lutdesc = m->msg<FUSE_lutdesc_message_t>();
00214         char lut_id[LUT_ID_MAX_LENGTH + 1];
00215         lut_id[LUT_ID_MAX_LENGTH] = 0;
00216         strncpy(lut_id, lutdesc->lut_id, LUT_ID_MAX_LENGTH);
00217         printf("LUT upload of %s has failed.\n", lut_id);
00218         __client->cancel();
00219       }
00220       break;
00221 
00222     default:
00223       printf("Unhandled message of type %u received\n", m->type());
00224       __client->cancel();
00225       break;
00226     }
00227   }
00228 
00229 
00230   virtual void all_for_now()
00231   {
00232     printf("All for now\n");
00233     __explore_mutex->lock();
00234     __explore_waitcond->wake_all();
00235     __explore_mutex->unlock();
00236   }
00237 
00238   virtual void cache_exhausted()
00239   {
00240   }
00241 
00242   virtual void browse_failed(const char *name,
00243                              const char *type,
00244                              const char *domain)
00245   {
00246     printf("Browsing for %s failed\n", type);
00247   }
00248 
00249   virtual void service_added(const char *name,
00250                              const char *type,
00251                              const char *domain,
00252                              const char *host_name,
00253                              const struct sockaddr *addr,
00254                              const socklen_t addr_size,
00255                              uint16_t port,
00256                              std::list<std::string> &txt,
00257                              int flags
00258                              )
00259   {
00260     struct sockaddr_in *s;
00261     if ( addr_size == sizeof(struct sockaddr_in) ) {
00262       s = (struct sockaddr_in *)addr;
00263     } else {
00264       printf("%s socket data not IPv4, ignoring\n", name);
00265       return;
00266     }
00267 
00268     char addrp[INET_ADDRSTRLEN];
00269     inet_ntop(AF_INET, &(s->sin_addr), addrp, sizeof(addrp));
00270     printf("Found %s%s%s (%s/%s on %hu), querying\n",
00271            c_blue, name, c_normal, host_name, addrp, port);
00272 
00273     __client = new FuseClient(host_name, port, this);
00274     __client->connect();
00275     __client->start();
00276     __client->wait_greeting();
00277     show_all();
00278     __client->join();
00279     delete __client;
00280 
00281     printf("\n");
00282   }
00283 
00284   virtual void service_removed(const char *name,
00285                                const char *type,
00286                                const char *domain)
00287   {
00288   }
00289 
00290   /** Print usage message. */
00291   void
00292   print_usage()
00293   {
00294     printf("Usage: %s -i/-c/-C/-s/-e [-n host[:port]/id file]\n"
00295            "  -i             Get image\n"
00296            "  -j             Get JPEG-compressed image\n"
00297            "  -c             Get colormap\n"
00298            "  -C             Set colormap from file\n"
00299            "  -s             Show available images and LUTs\n"
00300            "  -e             Explore network. Will query all instances of Fountain\n"
00301            "                 found on the network for all available images and LUTs.\n"
00302            "  -n net_string  Open network camera, the camera string is of the form\n"
00303            "                 host[:port]/id. You have to specify at least the host\n"
00304            "                 and the id, the port is optional and defaults to 5000\n"
00305            "                 Depending on the operation id is the image or the LUT ID\n"
00306            "  file           File to write incoming data to or to read data to send from\n",
00307            __argp->program_name());
00308   }
00309 
00310 
00311   /** Request image.
00312    * @param image_id Image ID.
00313    * @param jpeg if true JPEG images are requested, raw images otherwise
00314    */
00315   void
00316   get_image(const char *image_id, bool jpeg)
00317   {
00318     FUSE_imagereq_message_t *idm = (FUSE_imagereq_message_t *)malloc(sizeof(FUSE_imagereq_message_t));
00319     memset(idm, 0, sizeof(FUSE_imagereq_message_t));
00320     strncpy(idm->image_id, image_id, IMAGE_ID_MAX_LENGTH);
00321     idm->format = (jpeg ? FUSE_IF_JPEG : FUSE_IF_RAW);
00322     __client->enqueue(FUSE_MT_GET_IMAGE, idm, sizeof(FUSE_imagereq_message_t));
00323   }
00324 
00325   /** Request LUT.
00326    * @param lut_id LUT ID.
00327    */
00328   void
00329   get_colormap(const char *lut_id)
00330   {
00331     FUSE_lutdesc_message_t *ldm = (FUSE_lutdesc_message_t *)malloc(sizeof(FUSE_lutdesc_message_t));
00332     memset(ldm, 0, sizeof(FUSE_lutdesc_message_t));
00333     strncpy(ldm->lut_id, lut_id, LUT_ID_MAX_LENGTH);
00334     __client->enqueue(FUSE_MT_GET_LUT, ldm, sizeof(FUSE_lutdesc_message_t));
00335   }
00336 
00337   /** Upload LUT.
00338    * @param lut_id LUT ID.
00339    */
00340   void
00341   set_colormap(const char *lut_id)
00342   {
00343     ColormapFile cmf;
00344     cmf.read(__file);
00345     Colormap *cm = cmf.get_colormap();
00346     FuseLutContent *lc = new FuseLutContent(lut_id, cm->get_buffer(),
00347                                             cm->width(), cm->height(), cm->depth(),
00348                                             /* bytes per cell */ 1);
00349     delete cm;
00350 
00351     __client->enqueue(new FuseNetworkMessage(FUSE_MT_SET_LUT, lc));
00352   }
00353 
00354   /** Show all images and LUTs. */
00355   void
00356   show_all()
00357   {
00358     __client->enqueue(FUSE_MT_GET_IMAGE_LIST);
00359     __client->enqueue(FUSE_MT_GET_LUT_LIST);
00360   }
00361 
00362   /** Explore network.
00363    * This will query via service discovery for all Fountain instances on the local
00364    * network. It will then connect to each of these and query them for existing images
00365    * and lookup tables.
00366    */
00367   void
00368   explore_network()
00369   {
00370 #ifdef HAVE_AVAHI
00371     __exploring = true;
00372     __explore_mutex = new Mutex();
00373     __explore_waitcond = new WaitCondition(__explore_mutex);
00374 
00375     __explore_mutex->lock();
00376 
00377     __avahi_thread = new AvahiThread();
00378     __avahi_thread->start();
00379 
00380     __avahi_thread->watch_service("_fountain._tcp", this);
00381 
00382     __explore_waitcond->wait();
00383     delete __explore_waitcond;
00384     __explore_mutex->unlock();
00385     delete __explore_mutex;
00386     __avahi_thread->cancel();
00387     __avahi_thread->join();
00388     delete __avahi_thread;
00389 #else
00390     printf("\nExploration is not available because Avahi support is missing. "
00391            "Install avahi-devel and recompile.\n\n");
00392 #endif
00393   }
00394 
00395   /** Run. */
00396   void
00397   run()
00398   {
00399     if ( __argp->has_arg("h") ) {
00400       print_usage();
00401       exit(0);
00402     } else {
00403       char *net_string;
00404       if ( __argp->has_arg("n") ) {
00405         net_string = strdup(__argp->arg("n"));
00406       } else {
00407         net_string = strdup("localhost");
00408       }
00409       char *id = NULL, *host = NULL, *port = NULL, *save_ptr = NULL;
00410       int port_num = 2208;
00411       char *hostport;
00412       
00413       hostport = strtok_r(net_string, "/", &save_ptr);
00414       id = strtok_r(NULL, "", &save_ptr);
00415 
00416       if ( strchr(hostport, ':') != NULL ) {
00417         host = strtok_r(hostport, ":", &save_ptr);
00418         port = strtok_r(NULL, "", &save_ptr);
00419       } else {
00420         host = hostport;
00421       }
00422 
00423       if ( port != NULL ) {
00424         port_num = atoi(port);
00425         if ( (port_num < 0) || (port_num > 0xFFFF) ) {
00426           throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF);
00427         }
00428       }
00429 
00430       if (__argp->has_arg("i") || __argp->has_arg("j") ||
00431           __argp->has_arg("c") || __argp->has_arg("C")) {
00432         if ( __argp->num_items() == 0 ) {
00433           print_usage();
00434           printf("\nFile name missing\n\n");
00435           exit(1);
00436         } else {
00437           __file = __argp->items()[0];
00438         }
00439 
00440         if (id == NULL) {
00441           print_usage();
00442           printf("\nNo Image/LUT ID given, needed for -i/-c/-C\n\n");
00443           exit(2);
00444         }
00445       }
00446 
00447       if ( ! __argp->has_arg("e") ) {
00448         __client = new FuseClient(host, port_num, this);
00449         __client->connect();
00450         __client->start();
00451         __client->wait_greeting();
00452       }
00453 
00454       if ( __argp->has_arg("i") ) {
00455         get_image(id, /* JPEG? */ false);
00456       } else if ( __argp->has_arg("j") ) {
00457         get_image(id, /* JPEG? */ true);
00458       } else if ( __argp->has_arg("c") ) {
00459         get_colormap(id);
00460       } else if ( __argp->has_arg("C") ) {
00461         set_colormap(id);
00462       } else if ( __argp->has_arg("s") ) {
00463         show_all();
00464       } else if ( __argp->has_arg("e") ) {
00465         explore_network();
00466       } else {
00467         print_usage();
00468         __client->cancel();
00469       }
00470 
00471       if ( ! __argp->has_arg("e") ) {
00472         __client->join();
00473         delete __client;
00474       }
00475 
00476       free(net_string);
00477     }
00478   }
00479 
00480 private:
00481   ArgumentParser *__argp;
00482   FuseClient     *__client;
00483 
00484   const char     *__file;
00485 
00486   bool            __exploring;
00487   Mutex          *__explore_mutex;
00488   WaitCondition  *__explore_waitcond;
00489 
00490 #ifdef HAVE_AVAHI
00491   AvahiThread    *__avahi_thread;
00492 #endif
00493 };
00494 
00495 
00496 int
00497 main(int argc, char **argv)
00498 {
00499   ArgumentParser argp(argc, argv, "hn:icCsej");
00500 
00501   FireVisionNetworkTool *nettool = new FireVisionNetworkTool(&argp);
00502   nettool->run();
00503   delete nettool;
00504 
00505   return 0;
00506 }