00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00058 class FireVisionNetworkTool
00059 : public FuseClientHandler,
00060 public ServiceBrowseHandler
00061 {
00062 public:
00063
00064
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
00095
00096 switch (m->type() ) {
00097 case FUSE_MT_IMAGE:
00098
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
00171 try {
00172 FuseLutContent *lc = m->msgc<FuseLutContent>();
00173
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
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
00312
00313
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
00326
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
00338
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 1);
00349 delete cm;
00350
00351 __client->enqueue(new FuseNetworkMessage(FUSE_MT_SET_LUT, lc));
00352 }
00353
00354
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
00363
00364
00365
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
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, false);
00456 } else if ( __argp->has_arg("j") ) {
00457 get_image(id, 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 }