Fawkes API  Fawkes Development Version
net.cpp
1 
2 /***************************************************************************
3  * net.cpp - Generic network tool
4  *
5  * Created: Fri Nov 16 10:27:57 2007
6  * Copyright 2005-2009 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include <fvutils/net/fuse.h>
24 #include <fvutils/net/fuse_client.h>
25 #include <fvutils/net/fuse_client_handler.h>
26 #include <fvutils/net/fuse_message.h>
27 #include <fvutils/net/fuse_image_content.h>
28 #include <fvutils/net/fuse_lut_content.h>
29 #include <fvutils/net/fuse_imagelist_content.h>
30 #include <fvutils/net/fuse_lutlist_content.h>
31 #include <fvutils/writers/fvraw.h>
32 #include <fvutils/color/colorspaces.h>
33 #include <fvutils/colormap/yuvcm.h>
34 #include <fvutils/colormap/cmfile.h>
35 
36 #include <core/threading/mutex.h>
37 #include <core/threading/wait_condition.h>
38 #include <core/exceptions/software.h>
39 #include <utils/system/argparser.h>
40 #include <utils/system/console_colors.h>
41 
42 #include <netcomm/service_discovery/browse_handler.h>
43 #ifdef HAVE_AVAHI
44 #include <netcomm/dns-sd/avahi_thread.h>
45 #endif
46 
47 // for inet_ntop
48 #include <arpa/inet.h>
49 #include <netinet/in.h>
50 #include <cstring>
51 #include <cstdlib>
52 #include <cstdio>
53 
54 using namespace fawkes;
55 using namespace firevision;
56 
57 /** FireVision Network Tool */
59  : public FuseClientHandler,
61 {
62  public:
63  /** Constructor.
64  * @param argp argument parser
65  */
67  {
68  __argp = argp;
69  __exploring = false;
70  __explore_waitcond = NULL;
71  }
72 
73  void
74  fuse_invalid_server_version(uint32_t local_version,
75  uint32_t remote_version) throw()
76  {
77  printf("Invalid version received (local: %u, remote: %u)\n",
78  local_version, remote_version);
79  }
80 
81  virtual void
83  {
84  }
85 
86  virtual void
88  {
89  }
90 
91  virtual void
93  {
94  // printf("Received message of type %u\n", m->type());
95 
96  switch (m->type() ) {
97  case FUSE_MT_IMAGE:
98  // we got an image, save it to the given file
99  try {
100  FuseImageContent *ic = m->msgc<FuseImageContent>();
101  if ( ic->format() == FUSE_IF_RAW ) {
102  FvRawWriter *w = new FvRawWriter(__file, ic->pixel_width(), ic->pixel_height(),
103  (colorspace_t)ic->colorspace(), ic->buffer());
104  w->write();
105  delete w;
106  } else if ( ic->format() == FUSE_IF_JPEG ) {
107  FILE *f = fopen(__file, "w");
108  if (fwrite(ic->buffer(), ic->buffer_size(), 1, f) == 0) {
109  printf("Failed to write data to file");
110  }
111  fclose(f);
112  } else {
113  printf("Image of unknown format (%u) received.\n", ic->format());
114  }
115  delete ic;
116  } catch (Exception &e) {
117  printf("Received message cannot be casted to FuseImageMessage\n");
118  e.print_trace();
119  }
120  __client->cancel();
121  break;
122  case FUSE_MT_IMAGE_LIST:
123  try {
124  FuseImageListContent *ilc = m->msgc<FuseImageListContent>();
125  if ( ilc->has_next() ) {
126  printf("Available images:\n");
127  while ( ilc->has_next() ) {
128  FUSE_imageinfo_t *ii = ilc->next();
129  char tmp[IMAGE_ID_MAX_LENGTH + 1];
130  tmp[IMAGE_ID_MAX_LENGTH] = 0;
131  strncpy(tmp, ii->image_id, IMAGE_ID_MAX_LENGTH);
132  printf(" %s (%u x %u, %s)\n", tmp, ntohl(ii->width), ntohl(ii->height),
133  colorspace_to_string((colorspace_t)ntohs(ii->colorspace)));
134  }
135  } else {
136  printf("No images available\n");
137  }
138  delete ilc;
139  } catch (Exception &e) {
140  printf("Received message cannot be casted to FuseImageListMessage\n");
141  e.print_trace();
142  }
143  break;
144  case FUSE_MT_LUT_LIST:
145  try {
146  FuseLutListContent *llc = m->msgc<FuseLutListContent>();
147  if ( llc->has_next() ) {
148  printf("Available lookup tables:\n");
149  while ( llc->has_next() ) {
150  FUSE_lutinfo_t *li = llc->next();
151  char tmp[LUT_ID_MAX_LENGTH + 1];
152  tmp[LUT_ID_MAX_LENGTH] = 0;
153  strncpy(tmp, li->lut_id, LUT_ID_MAX_LENGTH);
154  printf(" %s (%u x %u x %u, %u bpc)\n", tmp,
155  ntohl(li->width), ntohl(li->height),
156  ntohl(li->depth), ntohl(li->bytes_per_cell));
157  }
158  } else {
159  printf("No lookup tables available\n");
160  }
161  delete llc;
162  } catch (Exception &e) {
163  printf("Received message cannot be casted to FuseImageListMessage\n");
164  e.print_trace();
165  }
166  __client->cancel();
167  break;
168 
169  case FUSE_MT_LUT:
170  // we got a LUT, save it to the given file
171  try {
172  FuseLutContent *lc = m->msgc<FuseLutContent>();
173  // Currently we expect colormaps, so make sure we get sensible dimensions
174  if ( lc->width() != 256 ) {
175  printf("Invalid dimensions for LUT received, colormap width %u != 256", lc->width());
176  } else if ( lc->height() != 256 ) {
177  printf("Invalid dimensions for LUT received, colormap height %u != 256", lc->height());
178  } else if ( lc->depth() > 256 ) {
179  printf("Invalid dimensions for LUT received, colormap depth %u > 256", lc->depth());
180  } else {
181  try {
182  YuvColormap yuvcm(lc->depth());
183  yuvcm.set(lc->buffer());
184  ColormapFile cmf;
185  cmf.add_colormap(&yuvcm);
186  cmf.write(__file);
187  } catch (Exception &e) {
188  e.append("Failed to save colormap");
189  e.print_trace();
190  }
191  }
192  delete lc;
193  } catch (Exception &e) {
194  printf("Received message cannot be casted to FuseLutMessage\n");
195  e.print_trace();
196  }
197  __client->cancel();
198  break;
199 
200  case FUSE_MT_SET_LUT_SUCCEEDED:
201  {
202  FUSE_lutdesc_message_t *lutdesc = m->msg<FUSE_lutdesc_message_t>();
203  char lut_id[LUT_ID_MAX_LENGTH + 1];
204  lut_id[LUT_ID_MAX_LENGTH] = 0;
205  strncpy(lut_id, lutdesc->lut_id, LUT_ID_MAX_LENGTH);
206  printf("LUT %s has been uploaded successfully.\n", lut_id);
207  __client->cancel();
208  }
209  break;
210 
211  case FUSE_MT_SET_LUT_FAILED:
212  {
213  FUSE_lutdesc_message_t *lutdesc = m->msg<FUSE_lutdesc_message_t>();
214  char lut_id[LUT_ID_MAX_LENGTH + 1];
215  lut_id[LUT_ID_MAX_LENGTH] = 0;
216  strncpy(lut_id, lutdesc->lut_id, LUT_ID_MAX_LENGTH);
217  printf("LUT upload of %s has failed.\n", lut_id);
218  __client->cancel();
219  }
220  break;
221 
222  default:
223  printf("Unhandled message of type %u received\n", m->type());
224  __client->cancel();
225  break;
226  }
227  }
228 
229 
230  virtual void all_for_now()
231  {
232  printf("All for now\n");
233  __explore_mutex->lock();
234  __explore_waitcond->wake_all();
235  __explore_mutex->unlock();
236  }
237 
238  virtual void cache_exhausted()
239  {
240  }
241 
242  virtual void browse_failed(const char *name,
243  const char *type,
244  const char *domain)
245  {
246  printf("Browsing for %s failed\n", type);
247  }
248 
249  virtual void service_added(const char *name,
250  const char *type,
251  const char *domain,
252  const char *host_name,
253  const char *interface,
254  const struct sockaddr *addr,
255  const socklen_t addr_size,
256  uint16_t port,
257  std::list<std::string> &txt,
258  int flags
259  )
260  {
261  struct sockaddr_in *s;
262  if ( addr_size == sizeof(struct sockaddr_in) ) {
263  s = (struct sockaddr_in *)addr;
264  } else {
265  printf("%s socket data not IPv4, ignoring\n", name);
266  return;
267  }
268 
269  char addrp[INET_ADDRSTRLEN];
270  inet_ntop(AF_INET, &(s->sin_addr), addrp, sizeof(addrp));
271  printf("Found %s%s%s (%s/%s on %hu), querying\n",
272  c_blue, name, c_normal, host_name, addrp, port);
273 
274  __client = new FuseClient(host_name, port, this);
275  __client->connect();
276  __client->start();
277  __client->wait_greeting();
278  show_all();
279  __client->join();
280  delete __client;
281 
282  printf("\n");
283  }
284 
285  virtual void service_removed(const char *name,
286  const char *type,
287  const char *domain)
288  {
289  }
290 
291  /** Print usage message. */
292  void
294  {
295  printf("Usage: %s -i/-c/-C/-s/-e [-n host[:port]/id file]\n"
296  " -i Get image\n"
297  " -j Get JPEG-compressed image\n"
298  " -c Get colormap\n"
299  " -C Set colormap from file\n"
300  " -s Show available images and LUTs\n"
301  " -e Explore network. Will query all instances of Fountain\n"
302  " found on the network for all available images and LUTs.\n"
303  " -n net_string Open network camera, the camera string is of the form\n"
304  " host[:port]/id. You have to specify at least the host\n"
305  " and the id, the port is optional and defaults to 5000\n"
306  " Depending on the operation id is the image or the LUT ID\n"
307  " file File to write incoming data to or to read data to send from\n",
308  __argp->program_name());
309  }
310 
311 
312  /** Request image.
313  * @param image_id Image ID.
314  * @param jpeg if true JPEG images are requested, raw images otherwise
315  */
316  void
317  get_image(const char *image_id, bool jpeg)
318  {
320  memset(idm, 0, sizeof(FUSE_imagereq_message_t));
321  strncpy(idm->image_id, image_id, IMAGE_ID_MAX_LENGTH);
322  idm->format = (jpeg ? FUSE_IF_JPEG : FUSE_IF_RAW);
323  __client->enqueue(FUSE_MT_GET_IMAGE, idm, sizeof(FUSE_imagereq_message_t));
324  }
325 
326  /** Request LUT.
327  * @param lut_id LUT ID.
328  */
329  void
330  get_colormap(const char *lut_id)
331  {
333  memset(ldm, 0, sizeof(FUSE_lutdesc_message_t));
334  strncpy(ldm->lut_id, lut_id, LUT_ID_MAX_LENGTH);
335  __client->enqueue(FUSE_MT_GET_LUT, ldm, sizeof(FUSE_lutdesc_message_t));
336  }
337 
338  /** Upload LUT.
339  * @param lut_id LUT ID.
340  */
341  void
342  set_colormap(const char *lut_id)
343  {
344  ColormapFile cmf;
345  cmf.read(__file);
346  Colormap *cm = cmf.get_colormap();
347  FuseLutContent *lc = new FuseLutContent(lut_id, cm->get_buffer(),
348  cm->width(), cm->height(), cm->depth(),
349  /* bytes per cell */ 1);
350  delete cm;
351 
352  __client->enqueue(new FuseNetworkMessage(FUSE_MT_SET_LUT, lc));
353  }
354 
355  /** Show all images and LUTs. */
356  void
358  {
359  __client->enqueue(FUSE_MT_GET_IMAGE_LIST);
360  __client->enqueue(FUSE_MT_GET_LUT_LIST);
361  }
362 
363  /** Explore network.
364  * This will query via service discovery for all Fountain instances on the local
365  * network. It will then connect to each of these and query them for existing images
366  * and lookup tables.
367  */
368  void
370  {
371 #ifdef HAVE_AVAHI
372  __exploring = true;
373  __explore_mutex = new Mutex();
374  __explore_waitcond = new WaitCondition(__explore_mutex);
375 
376  __explore_mutex->lock();
377 
378  __avahi_thread = new AvahiThread();
379  __avahi_thread->start();
380 
381  __avahi_thread->watch_service("_fountain._tcp", this);
382 
383  __explore_waitcond->wait();
384  delete __explore_waitcond;
385  __explore_mutex->unlock();
386  delete __explore_mutex;
387  __avahi_thread->cancel();
388  __avahi_thread->join();
389  delete __avahi_thread;
390 #else
391  printf("\nExploration is not available because Avahi support is missing. "
392  "Install avahi-devel and recompile.\n\n");
393 #endif
394  }
395 
396  /** Run. */
397  void
398  run()
399  {
400  if ( __argp->has_arg("h") ) {
401  print_usage();
402  exit(0);
403  } else {
404  char *net_string;
405  if ( __argp->has_arg("n") ) {
406  net_string = strdup(__argp->arg("n"));
407  } else {
408  net_string = strdup("localhost");
409  }
410  char *id = NULL, *host = NULL, *port = NULL, *save_ptr = NULL;
411  int port_num = 2208;
412  char *hostport;
413 
414  hostport = strtok_r(net_string, "/", &save_ptr);
415  id = strtok_r(NULL, "", &save_ptr);
416 
417  if ( strchr(hostport, ':') != NULL ) {
418  host = strtok_r(hostport, ":", &save_ptr);
419  port = strtok_r(NULL, "", &save_ptr);
420  } else {
421  host = hostport;
422  }
423 
424  if ( port != NULL ) {
425  port_num = atoi(port);
426  if ( (port_num < 0) || (port_num > 0xFFFF) ) {
427  throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF);
428  }
429  }
430 
431  if (__argp->has_arg("i") || __argp->has_arg("j") ||
432  __argp->has_arg("c") || __argp->has_arg("C")) {
433  if ( __argp->num_items() == 0 ) {
434  print_usage();
435  printf("\nFile name missing\n\n");
436  exit(1);
437  } else {
438  __file = __argp->items()[0];
439  }
440 
441  if (id == NULL) {
442  print_usage();
443  printf("\nNo Image/LUT ID given, needed for -i/-c/-C\n\n");
444  exit(2);
445  }
446  }
447 
448  if ( ! __argp->has_arg("e") ) {
449  __client = new FuseClient(host, port_num, this);
450  __client->connect();
451  __client->start();
452  __client->wait_greeting();
453  }
454 
455  if ( __argp->has_arg("i") ) {
456  get_image(id, /* JPEG? */ false);
457  } else if ( __argp->has_arg("j") ) {
458  get_image(id, /* JPEG? */ true);
459  } else if ( __argp->has_arg("c") ) {
460  get_colormap(id);
461  } else if ( __argp->has_arg("C") ) {
462  set_colormap(id);
463  } else if ( __argp->has_arg("s") ) {
464  show_all();
465  } else if ( __argp->has_arg("e") ) {
466  explore_network();
467  } else {
468  print_usage();
469  __client->cancel();
470  }
471 
472  if ( ! __argp->has_arg("e") ) {
473  __client->join();
474  delete __client;
475  }
476 
477  free(net_string);
478  }
479  }
480 
481 private:
482  ArgumentParser *__argp;
483  FuseClient *__client;
484 
485  const char *__file;
486 
487  bool __exploring;
488  Mutex *__explore_mutex;
489  WaitCondition *__explore_waitcond;
490 
491 #ifdef HAVE_AVAHI
492  AvahiThread *__avahi_thread;
493 #endif
494 };
495 
496 
497 int
498 main(int argc, char **argv)
499 {
500  ArgumentParser argp(argc, argv, "hn:icCsej");
501 
502  FireVisionNetworkTool *nettool = new FireVisionNetworkTool(&argp);
503  nettool->run();
504  delete nettool;
505 
506  return 0;
507 }
uint32_t bytes_per_cell
bytes per cell
Definition: fuse.h:180
void get_colormap(const char *lut_id)
Request LUT.
Definition: net.cpp:330
Wait until a given condition holds.
FUSE lookup table content.
unsigned int depth() const
Depth of LUT.
void add_colormap(Colormap *colormap)
Add colormap.
Definition: cmfile.cpp:98
Image request message.
Definition: fuse.h:147
void fuse_invalid_server_version(uint32_t local_version, uint32_t remote_version)
Invalid version string received.
Definition: net.cpp:74
Image info message.
Definition: fuse.h:165
virtual unsigned int depth() const =0
Get depth of colormap.
Fawkes library namespace.
virtual void all_for_now()
All results have been retrieved.
Definition: net.cpp:230
unsigned char * buffer() const
Image buffer.
unsigned int width() const
Width of LUT.
void get_image(const char *image_id, bool jpeg)
Request image.
Definition: net.cpp:317
uint32_t height
height of LUT
Definition: fuse.h:178
Parse command line arguments.
Definition: argparser.h:66
unsigned char * buffer() const
Get buffer.
Colormap interface.
Definition: colormap.h:38
virtual unsigned int height() const =0
Get height of colormap.
FUSE_lutinfo_t * next()
Get next LUT info.
static const char * c_blue
Print blue on console.
unsigned int pixel_width() const
Get image width.
uint32_t width
width of LUT
Definition: fuse.h:177
Colormap * get_colormap()
Get a freshly generated colormap based on current file content.
Definition: cmfile.cpp:169
YUV Colormap.
Definition: yuvcm.h:39
Interface for class that process browse results.
uint32_t width
width in pixels
Definition: fuse.h:169
virtual void fuse_connection_established()
Connection has been established.
Definition: net.cpp:82
virtual void service_removed(const char *name, const char *type, const char *domain)
A service has been removed from the network.
Definition: net.cpp:285
uint32_t colorspace
color space
Definition: fuse.h:167
unsigned int pixel_height() const
Get image height.
virtual void write(const char *file_name)
Write file.
Definition: fvfile.cpp:262
virtual void read(const char *file_name)
Read file.
Definition: fvfile.cpp:308
void set_colormap(const char *lut_id)
Upload LUT.
Definition: net.cpp:342
char image_id[IMAGE_ID_MAX_LENGTH]
image ID
Definition: fuse.h:166
Colormap file.
Definition: cmfile.h:55
virtual void service_added(const char *name, const char *type, const char *domain, const char *host_name, const char *interface, const struct sockaddr *addr, const socklen_t addr_size, uint16_t port, std::list< std::string > &txt, int flags)
A service has been announced on the network.
Definition: net.cpp:249
unsigned int height() const
Height of LUT.
void run()
Run.
Definition: net.cpp:398
FUSE Network Message.
Definition: fuse_message.h:41
FvRaw Writer implementation.
Definition: fvraw.h:34
FireVisionNetworkTool(ArgumentParser *argp)
Constructor.
Definition: net.cpp:66
Base class for exceptions in Fawkes.
Definition: exception.h:36
size_t buffer_size() const
Get size of buffer.
LUT info message.
Definition: fuse.h:175
char lut_id[LUT_ID_MAX_LENGTH]
LUT ID.
Definition: fuse.h:176
FUSE lookup table list content.
unsigned int format() const
Get image format.
virtual void set(unsigned int y, unsigned int u, unsigned int v, color_t c)
Set color class for given YUV value.
Definition: yuvcm.cpp:182
virtual void write()
Write to file.
Definition: fvraw.cpp:128
bool has_next()
Check if another image info is available.
char image_id[IMAGE_ID_MAX_LENGTH]
image ID
Definition: fuse.h:148
Avahi main thread.
Definition: avahi_thread.h:54
void explore_network()
Explore network.
Definition: net.cpp:369
unsigned int colorspace() const
Get colorspace.
virtual void fuse_connection_died()
Connection died.
Definition: net.cpp:87
virtual unsigned int width() const =0
Get width of colormap.
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:619
char lut_id[LUT_ID_MAX_LENGTH]
LUT ID.
Definition: fuse.h:161
uint32_t format
requested image format, see FUSE_image_format_t
Definition: fuse.h:149
virtual void cache_exhausted()
Cache exhausted.
Definition: net.cpp:238
uint32_t height
height in pixels
Definition: fuse.h:170
bool has_next()
Check if another LUT info is available.
void print_usage()
Print usage message.
Definition: net.cpp:293
virtual unsigned char * get_buffer() const =0
Get the raw buffer of this colormap.
virtual void fuse_inbound_received(FuseNetworkMessage *m)
Message received.
Definition: net.cpp:92
virtual void browse_failed(const char *name, const char *type, const char *domain)
Failed to browse for a given service.
Definition: net.cpp:242
Mutex mutual exclusion lock.
Definition: mutex.h:32
Index out of bounds.
Definition: software.h:88
uint32_t depth
depth of LUT
Definition: fuse.h:179
FireVision Network Tool.
Definition: net.cpp:58
FUSE_imageinfo_t * next()
Get next image info.
static const char * c_normal
Print normal on console, without colors, depends on console settings.
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:341
LUT description message.
Definition: fuse.h:160
void show_all()
Show all images and LUTs.
Definition: net.cpp:357