device_list.cc
Go to the documentation of this file.
1 /*
2  * Roboception GmbH
3  * Munich, Germany
4  * www.roboception.com
5  *
6  * Copyright (c) 2024 Roboception GmbH
7  * All rights reserved
8  *
9  * Author: Heiko Hirschmueller
10  */
11 
12 #include "device_list.h"
13 
14 #include <FL/fl_draw.H>
15 #include <FL/fl_ask.H>
16 #include <FL/filename.H>
17 
18 #include <sstream>
19 
20 DeviceList::DeviceList(int x, int y, int w, int h) : Fl_Table_Row(x, y, w, h)
21 {
22  type(SELECT_SINGLE);
23  color(FL_WHITE);
24 
25  tooltip("Double-click row to open WebGUI in browser.");
26 
27  cb=0;
28  user=0;
29  new_discovery=false;
30 
31  sort_col=0;
32  sort_down=true;
33  filter_rc=true;
34 
35  cols(8);
36  col_header(1);
37  col_header_height(24);
38  col_resize_min(10);
39 
40  col_width(0, 110);
41  col_width(1, 170);
42  col_width(2, 180);
43  col_width(3, 140);
44  col_width(4, 130);
45  col_width(5, 160);
46  col_width(6, 160);
47  col_width(7, 100);
48 
49  col_resize(1);
50 
51  rows(0);
52  row_header(0);
53  row_height_all(24);
54  row_resize(0);
55 
56  end();
57 }
58 
60 {
61  // store previous MAC addresses for finding out which devices are new
62 
63  previous_mac_list.clear();
64 
65  for (size_t i=0; i<device.size(); i++)
66  {
67  previous_mac_list.insert(device[i].item[5]);
68  }
69 
70  new_discovery=false;
71 
72  // clear table
73 
74  device.clear();
75  index.clear();
76  rows(0);
77 }
78 
79 void DeviceList::add(const char *name, const char *manufacturer, const char *model, const char *sn,
80  const char *ip, const char *mac, const char *interface, bool reachable)
81 {
82  // check if device with this mac address already exists
83 
84  int k=-1;
85  for (size_t i=0; i<device.size(); i++)
86  {
87  if (device[i].item[5].compare(mac) == 0)
88  {
89  k=static_cast<int>(i);
90  break;
91  }
92  }
93 
94  if (k >= 0)
95  {
96  // insert interface if it does not already exist
97 
98  if (device[k].interface_list.insert(interface).second)
99  {
100  // rebuild human readable item
101 
102  std::ostringstream out;
103 
104  bool first=true;
105  for (const auto &it: device[k].interface_list)
106  {
107  if (!first) out << ", ";
108  out << it;
109  first=false;
110  }
111 
112  device[k].item[6]=out.str();
113  device[k].reachable|=reachable;
114 
115  if (device[k].reachable)
116  {
117  device[k].item[7]="\u2713";
118  }
119  else
120  {
121  device[k].item[7]="\u2717";
122  }
123 
124  redraw();
125  }
126  }
127  else
128  {
129  // add as new device
130 
131  DeviceListData data;
132  data.item[0]=name;
133  data.item[1]=manufacturer;
134  data.item[2]=model;
135  data.item[3]=sn;
136  data.item[4]=ip;
137  data.item[5]=mac;
138  data.item[6]=interface;
139 
140  data.interface_list.insert(interface);
141 
142  if (reachable)
143  {
144  data.item[7]=u8"\u2713";
145  }
146  else
147  {
148  data.item[7]=u8"\u2717";
149  }
150 
151  data.reachable=reachable;
152  data.new_discovery=(previous_mac_list.size() > 0 && previous_mac_list.find(mac) == previous_mac_list.end());
154 
155  device.push_back(data);
156 
157  if (addDeviceIndex(device.size()-1))
158  {
159  rows(static_cast<int>(index.size()));
160  }
161  }
162 }
163 
165 {
166  int sel=-1;
167  for (size_t i=0; i<index.size(); i++)
168  {
169  if (row_selected(static_cast<int>(i)))
170  {
171  sel=static_cast<int>(i);
172  }
173  }
174 
175  return sel;
176 }
177 
179 {
180  int sel=getSelectedRow();
181  return (sel >= 0 && device[index[sel]].reachable &&
182  device[index[sel]].item[2].compare(0, 3, "rc_") == 0);
183 }
184 
186 {
187  int sel=getSelectedRow();
188  return (sel >= 0 && device[index[sel]].item[2].compare(0, 9, "rc_visard") == 0);
189 }
190 
192 {
193  int sel=getSelectedRow();
194 
195  if (sel >= 0)
196  {
197  return device[index[sel]].item[5];
198  }
199 
200  return std::string();
201 }
202 
203 std::vector<std::pair<std::string, std::string> > DeviceList::getCurrentNameMACList(bool only_rc_visard)
204 {
205  std::vector<std::pair<std::string, std::string> > ret;
206 
207  for (size_t i=0; i<index.size(); i++)
208  {
209  if (!only_rc_visard || device[index[i]].item[2].compare(0, 9, "rc_visard") == 0)
210  {
211  ret.push_back(std::pair<std::string, std::string>(device[index[i]].item[0], device[index[i]].item[5]));
212  }
213  }
214 
215  return ret;
216 }
217 
218 std::string DeviceList::getCell(int r, int c)
219 {
220  if (r >= 0 && r < static_cast<int>(index.size()) && c >= 0 && c < 7)
221  {
222  return device[index[r]].item[c];
223  }
224 
225  return std::string();
226 }
227 
229 {
230  // only for reachable rc devices
231 
232  if (device[index[r]].reachable && device[index[r]].item[2].compare(0, 3, "rc_") == 0)
233  {
234  // open Web GUI
235 
236  std::ostringstream out;
237 
238  out << "http://" << device[index[r]].item[4].c_str();
239 
240  char msg[160];
241  if (fl_open_uri(out.str().c_str(), msg, 160) == 0)
242  {
243  msg[159]='\0';
244  fl_alert("%s", msg);
245  }
246  }
247 }
248 
249 void DeviceList::filterRCDevices(bool _filter_rc)
250 {
251  filter_rc=_filter_rc;
253  rows(static_cast<int>(index.size()));
254  select_row(0, 0);
256  redraw();
257 }
258 
259 void DeviceList::filter(const char *_filter_value)
260 {
261  filter_value=_filter_value;
263  rows(static_cast<int>(index.size()));
264  select_row(0, 0);
266  redraw();
267 }
268 
269 void DeviceList::getSorting(int &_sort_col, bool &_sort_down)
270 {
271  if (_sort_col >= 0 && _sort_col < 8)
272  {
273  _sort_col=sort_col;
274  _sort_down=sort_down;
275  }
276 }
277 
278 void DeviceList::setSorting(int _sort_col, bool _sort_down)
279 {
280  sort_col=_sort_col;
281  sort_down=_sort_down;
282 
284  select_row(0, 0);
285  redraw();
286 }
287 
288 int DeviceList::handle(int event)
289 {
290  // call handle of parent
291 
292  int ret=Fl_Table_Row::handle(event);
293 
294  if (Fl::event() == FL_PUSH)
295  {
296  if (new_discovery && callback_context() == CONTEXT_CELL)
297  {
298  // remove highlighting newly discovered devices on first click
299 
300  new_discovery=false;
301  for (size_t i=0; i<device.size(); i++)
302  {
303  device[i].new_discovery=false;
304  }
305 
306  redraw();
307  }
308 
309  if (Fl::event_button() == FL_LEFT_MOUSE)
310  {
311  if (callback_context() == CONTEXT_COL_HEADER)
312  {
313  // click over header is used for sorting
314 
315  sortColumn(callback_col());
316  ret=1;
317  }
318 
319  if (Fl::event_clicks() == 1 && callback_context() == CONTEXT_CELL)
320  {
321  // double click on row opens the web gui
322 
323  openWebGUI(callback_row());
324  ret=1;
325  }
326  }
327  else if (Fl::event_button() == FL_MIDDLE_MOUSE)
328  {
329  if (callback_context() == CONTEXT_CELL)
330  {
331  // middle click on cell is used to copy cell content to selection buffer
332 
333  std::string value=getCell(callback_row(), callback_col());
334  select_row(callback_row());
335 
336  Fl::copy(value.c_str(), static_cast<int>(value.size()), 0);
337  }
338  else
339  {
340  select_row(0, 0);
341  }
342 
343  ret=1;
344  }
345 
347  }
348  else if (Fl::event() == FL_RELEASE && Fl::event_button() == FL_LEFT_MOUSE)
349  {
351  }
352 
353  return ret;
354 }
355 
356 void DeviceList::draw_cell(TableContext context, int ROW, int COL, int X, int Y, int W, int H)
357 {
358  switch (context)
359  {
360  case CONTEXT_STARTPAGE:
361  fl_font(FL_HELVETICA, 14);
362  break;
363 
364  case CONTEXT_RC_RESIZE:
365  if (cols() > 0)
366  {
367  int cw=0;
368  for (int i=0; i<cols()-1; i++)
369  {
370  cw+=col_width(i);
371  }
372 
373  int last_col=cols()-1;
374  if (cw+10 < tiw)
375  {
376  if (cw+col_width(last_col) != tiw)
377  {
378  col_width(last_col, tiw-cw);
379  }
380  }
381  }
382  break;
383 
384  case CONTEXT_COL_HEADER:
385  {
386  static const char *header[]={"Name", "Manufacturer", "Model", "Serial",
387  "IP Address", "MAC Address", "Interface(s)", "Reachable"};
388 
389  fl_push_clip(X, Y, W, H);
390  fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, row_header_color());
391  fl_color(FL_INACTIVE_COLOR);
392 
393  if (sort_col == COL)
394  {
395  // show that this column is used for sorting and show direction
396 
397  std::ostringstream out;
398  out << header[COL] << ' ';
399 
400  if (sort_down)
401  {
402  out << "@-22>";
403  }
404  else
405  {
406  out << "@-22<";
407  }
408 
409  fl_draw(out.str().c_str(), X, Y, W, H, FL_ALIGN_LEFT);
410  }
411  else
412  {
413  fl_draw(header[COL], X, Y, W, H, FL_ALIGN_LEFT);
414  }
415 
416  fl_pop_clip();
417  }
418  break;
419 
420  case CONTEXT_CELL:
421  {
422  fl_push_clip(X, Y, W, H);
423 
424  if (row_selected(ROW))
425  {
426 // fl_color(FL_SELECTION_COLOR);
427  fl_color(0x0c84a200); // use Roboception color for selection
428  }
429  else if (device[index[ROW]].new_discovery)
430  {
431  fl_color(FL_DARK3);
432  }
433  else
434  {
435  fl_color(FL_WHITE);
436  }
437 
438  fl_rectf(X, Y, W, H);
439 
440  if (row_selected(ROW))
441  {
442  fl_color(FL_WHITE);
443  }
444  else
445  {
446  fl_color(0x20202000); // dark gray looks a bit better
447  }
448 
449  if (COL < 7)
450  {
451  fl_draw(device[index[ROW]].item[COL].c_str(), X+3, Y, W, H, FL_ALIGN_LEFT);
452  }
453  else
454  {
455  fl_draw(device[index[ROW]].item[COL].c_str(), X+3, Y, W, H, FL_ALIGN_CENTER);
456  }
457 
458  fl_color(color());
459  fl_rect(X, Y, W, H);
460  fl_pop_clip();
461  }
462  break;
463 
464  default:
465  break;
466  }
467 }
468 
470 {
471  if (cb)
472  {
473  cb(this, user);
474  }
475 }
476 
478 {
479  if (sort_col == c)
480  {
481  // toggle sorting direction
483  }
484  else
485  {
486  // sort new column
487  sort_col=c;
488  sort_down=true;
489  }
490 
492  select_row(0, 0);
493  redraw();
494 }
495 
497 {
498  // check if device is visible according to filter criteria
499 
500  if (filter_rc && device[i].item[2].compare(0, 3, "rc_") != 0) // only rc devices
501  {
502  return false;
503  }
504 
505  if (filter_value.size() > 0)
506  {
507  bool found=false;
508  for (int k=0; k<7 && !found; k++)
509  {
510  found=(device[i].item[k].find(filter_value) != std::string::npos);
511  }
512 
513  if (!found)
514  {
515  return false;
516  }
517  }
518 
519  // insert index according to sorting
520 
521  int dir=1;
522  if (!sort_down)
523  {
524  dir=-1;
525  }
526 
527  size_t k=0;
528  while (k < index.size())
529  {
530  if (device[i].item[sort_col].compare(device[index[k]].item[sort_col])*dir < 0)
531  {
532  index.insert(index.begin()+k, i);
533  break;
534  }
535 
536  k++;
537  }
538 
539  if (k >= index.size())
540  {
541  index.push_back(i);
542  }
543 
544  return true;
545 }
546 
548 {
549  // rebuilding the list of indices
550 
551  index.clear();
552 
553  for (size_t i=0; i<device.size(); i++)
554  {
555  addDeviceIndex(i);
556  }
557 }
DeviceList::addDeviceIndex
bool addDeviceIndex(size_t i)
Definition: device_list.cc:496
DeviceList::getSorting
void getSorting(int &sort_col, bool &sort_down)
Definition: device_list.cc:269
DeviceList::setSorting
void setSorting(int sort_col, bool sort_down)
Definition: device_list.cc:278
DeviceList::sort_down
bool sort_down
Definition: device_list.h:82
DeviceList::isReachableRCDeviceSelected
bool isReachableRCDeviceSelected()
Definition: device_list.cc:178
device_list.h
DeviceList::DeviceListData::item
std::string item[8]
Definition: device_list.h:69
DeviceList::draw_cell
void draw_cell(TableContext context, int ROW, int COL, int X, int Y, int W, int H)
Definition: device_list.cc:356
DeviceList::DeviceListData::reachable
bool reachable
Definition: device_list.h:71
DeviceList::index
std::vector< size_t > index
Definition: device_list.h:76
DeviceList::device
std::vector< DeviceListData > device
Definition: device_list.h:75
DeviceList::clear
void clear()
Definition: device_list.cc:59
DeviceList::DeviceListData::interface_list
std::set< std::string > interface_list
Definition: device_list.h:70
DeviceList::DeviceList
DeviceList(int x, int y, int w, int h)
Definition: device_list.cc:20
DeviceList::cb
Fl_Callback * cb
Definition: device_list.h:78
DeviceList::filter
void filter(const char *filter_value)
Definition: device_list.cc:259
DeviceList::openWebGUI
void openWebGUI(int r)
Definition: device_list.cc:228
DeviceList::DeviceListData::new_discovery
bool new_discovery
Definition: device_list.h:72
DeviceList::add
void add(const char *name, const char *manufacturer, const char *model, const char *sn, const char *ip, const char *mac, const char *interface, bool reachable)
Definition: device_list.cc:79
DeviceList::getSelectedRow
int getSelectedRow()
Definition: device_list.cc:164
DeviceList::DeviceListData
Definition: device_list.h:67
DeviceList::notifySelectionChange
void notifySelectionChange()
Definition: device_list.cc:469
DeviceList::previous_mac_list
std::set< std::string > previous_mac_list
Definition: device_list.h:64
DeviceList::new_discovery
bool new_discovery
Definition: device_list.h:65
DeviceList::isRCVisardSelected
bool isRCVisardSelected()
Definition: device_list.cc:185
DeviceList::getCell
std::string getCell(int r, int c)
Definition: device_list.cc:218
DeviceList::filter_rc
bool filter_rc
Definition: device_list.h:83
DeviceList::updateDeviceIndices
void updateDeviceIndices()
Definition: device_list.cc:547
DeviceList::getCurrentNameMACList
std::vector< std::pair< std::string, std::string > > getCurrentNameMACList(bool only_rc_visard)
Definition: device_list.cc:203
DeviceList::handle
int handle(int event)
Definition: device_list.cc:288
DeviceList::filterRCDevices
void filterRCDevices(bool filter_rc)
Definition: device_list.cc:249
DeviceList::getSelectedMAC
std::string getSelectedMAC()
Definition: device_list.cc:191
DeviceList::sort_col
int sort_col
Definition: device_list.h:81
DeviceList::filter_value
std::string filter_value
Definition: device_list.h:84
DeviceList::sortColumn
void sortColumn(int c)
Definition: device_list.cc:477
DeviceList::user
void * user
Definition: device_list.h:79


rcdiscover
Author(s): Heiko Hirschmueller , Raphael Schaller
autogenerated on Thu Aug 1 2024 02:55:56