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