Fawkes API
Fawkes Development Version
|
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 }