implement receiving packets
Stefan Schuermans

Stefan Schuermans commited on 2017-06-11 15:48:14
Showing 11 changed files, with 364 additions and 15 deletions.

... ...
@@ -48,6 +48,48 @@ Config::Config(std::string const &configFile):
48 48
   readFile(configFile);
49 49
 }
50 50
 
51
+/**
52
+ * @brief create sockets and listen for incoming packets
53
+ * @throws std::exception in case of error
54
+ */
55
+void Config::start()
56
+{
57
+  try {
58
+    DistriMap::iterator dist;
59
+    for (dist = m_distriMap.begin(); dist != m_distriMap.end(); ++dist) {
60
+      Distri &distri = dist->second;
61
+      distri.start();
62
+    }
63
+  } catch (...) {
64
+    stop();
65
+    throw;
66
+  }
67
+}
68
+
69
+/**
70
+ * @brief stop listening for incoming packets and close sockets
71
+ */
72
+void Config::stop()
73
+{
74
+  DistriMap::iterator dist;
75
+  for (dist = m_distriMap.begin(); dist != m_distriMap.end(); ++dist) {
76
+    Distri &distri = dist->second;
77
+    distri.stop();
78
+  }
79
+}
80
+
81
+/// get and reset packet counter of all distributors
82
+unsigned long Config::getPacketCounterAndReset()
83
+{
84
+  unsigned long sum = 0;
85
+  DistriMap::iterator dist;
86
+  for (dist = m_distriMap.begin(); dist != m_distriMap.end(); ++dist) {
87
+    Distri &distri = dist->second;
88
+    sum += distri.getPacketCounterAndReset();
89
+  }
90
+  return sum;
91
+}
92
+
51 93
 /**
52 94
  * @brief draw pixels of all distributors
53 95
  * @param[in] cairo cairo context for drawing
... ...
@@ -43,6 +43,20 @@ public:
43 43
   /// get bounding box
44 44
   BBox const &getBBox() const { return m_bb; }
45 45
 
46
+  /**
47
+   * @brief create sockets and listen for incoming packets
48
+   * @throws std::exception in case of error
49
+   */
50
+  void start();
51
+
52
+  /**
53
+   * @brief stop listening for incoming packets and close sockets
54
+   */
55
+  void stop();
56
+
57
+  /// get and reset packet counter of all distributors
58
+  unsigned long getPacketCounterAndReset();
59
+
46 60
   /**
47 61
    * @brief draw pixels of all distributors
48 62
    * @param[in] cairo cairo context for drawing
... ...
@@ -18,10 +18,13 @@
18 18
  */
19 19
 
20 20
 #include <arpa/inet.h>
21
+#include <errno.h>
21 22
 #include <gtkmm.h>
22 23
 #include <map>
23 24
 #include <sstream>
24 25
 #include <stdexcept>
26
+#include <stdint.h>
27
+#include <string.h>
25 28
 #include <vector>
26 29
 
27 30
 #include "bbox.h"
... ...
@@ -36,8 +39,20 @@ Distri::Distri():
36 39
   m_outputs(0),
37 40
   m_pixels(0),
38 41
   m_mappings(),
39
-  m_outputMap()
42
+  m_outputMap(),
43
+  m_sock(-1),
44
+  m_packets(0)
40 45
 {
46
+  // initialize address to localhost and automatic port
47
+  m_addr.sin_family = AF_INET;
48
+  m_addr.sin_addr.s_addr = INADDR_LOOPBACK;
49
+  m_addr.sin_port = htons(0);
50
+}
51
+
52
+/// destructor
53
+Distri::~Distri()
54
+{
55
+  stop();
41 56
 }
42 57
 
43 58
 /**
... ...
@@ -161,6 +176,74 @@ void Distri::updateBBox(BBox &bb) const
161 176
   }
162 177
 }
163 178
 
179
+/**
180
+ * @brief create socket and listen for incoming packets
181
+ * @throws std::exception in case of error
182
+ */
183
+void Distri::start()
184
+{
185
+  // leave if  socket already exists
186
+  if (m_sock != -1) {
187
+    return;
188
+  }
189
+
190
+  // create socket
191
+  m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
192
+  if (m_sock == -1) {
193
+    std::stringstream msg;
194
+    msg << "could not create IPv4/UDP socket: " << strerror(errno);
195
+    throw::std::runtime_error(msg.str());
196
+  }
197
+  try {
198
+
199
+    // bind socket
200
+    if (bind(m_sock, (struct sockaddr *)&m_addr, sizeof(m_addr)) != 0) {
201
+      std::stringstream msg;
202
+      msg << "could not bind IPv4/UDP socket to \""
203
+          << inet_ntoa(m_addr.sin_addr) << ":" << ntohs(m_addr.sin_port)
204
+          << "\": " << strerror(errno);
205
+      throw::std::runtime_error(msg.str());
206
+    }
207
+
208
+    // register callback on incomimg packets
209
+    m_conn = Glib::signal_io().connect(sigc::mem_fun(*this, &Distri::on_packet),
210
+                                       m_sock, Glib::IO_IN);
211
+
212
+  } catch (...) {
213
+    close(m_sock);
214
+    m_sock = -1;
215
+    throw;
216
+  }
217
+}
218
+
219
+/**
220
+ * @brief stop listening for incoming packets and close socket
221
+ */
222
+void Distri::stop()
223
+{
224
+  // leave if no socket
225
+  if (m_sock == -1) {
226
+    return;
227
+  }
228
+
229
+  // uninstall callback
230
+  m_conn.disconnect();
231
+
232
+  // close socket
233
+  close(m_sock);
234
+
235
+  // socket in no more there
236
+  m_sock = -1;
237
+}
238
+
239
+/// get and reset packet conter
240
+unsigned long Distri::getPacketCounterAndReset()
241
+{
242
+  unsigned long packets = m_packets;
243
+  m_packets = 0;
244
+  return packets;
245
+}
246
+
164 247
 /**
165 248
  * @brief draw pixels of this distributor
166 249
  * @param[in] cairo cairo context for drawing
... ...
@@ -180,3 +263,86 @@ void Distri::draw(Cairo::RefPtr<Cairo::Context> &cairo,
180 263
   }
181 264
 }
182 265
 
266
+/**
267
+ * @brief callback on packet input on socket
268
+ */
269
+bool Distri::on_packet(Glib::IOCondition condition)
270
+{
271
+  // incoming packet
272
+  if (condition & Glib::IO_IN) {
273
+
274
+    // read packet
275
+    uint8_t buffer[65536];
276
+    ssize_t sz = recv(m_sock, buffer, sizeof(buffer), 0);
277
+    // success and long enough for header
278
+    if (sz >= 12) {
279
+      // check magic, 3 channels and maxvall 0xFF
280
+      if (buffer[ 0] == 0x23 && buffer[ 1] == 0x54 &&
281
+          buffer[ 2] == 0x26 && buffer[ 3] == 0x66 &&
282
+          buffer[ 8] == 0x00 && buffer[ 9] == 0x03 &&
283
+          buffer[10] == 0x00 && buffer[11] == 0xFF) {
284
+        // get outputs (height) and pixels (width)
285
+        unsigned int outputs = (unsigned int)buffer[4] << 8 |
286
+                               (unsigned int)buffer[5];
287
+        unsigned int pixels = (unsigned int)buffer[6] << 8 |
288
+                              (unsigned int)buffer[7];
289
+        // check data length
290
+        if (sz >= 12 + outputs * pixels * 3) {
291
+          uint8_t const *data = buffer + 12;
292
+          // process pixel data
293
+          procPixelData(outputs, pixels, data);
294
+        }
295
+      }
296
+    }
297
+
298
+  } // if IO_IN
299
+
300
+  // keep callback installed
301
+  return true;
302
+}
303
+
304
+/**
305
+ * @brief process pixel data
306
+ * @param[in] outputs number of outputs in pixel data
307
+ * @param[in] pixels number of pixels per output in pixel data
308
+ * @param[in] data pixel data
309
+ */
310
+void Distri::procPixelData(unsigned long outputs, unsigned long pixels,
311
+                           uint8_t const *data)
312
+{
313
+  // get mappings
314
+  Mapping const & mapRed   = m_mappings[Mapping::Red  ];
315
+  Mapping const & mapGreen = m_mappings[Mapping::Green];
316
+  Mapping const & mapBlue  = m_mappings[Mapping::Blue ];
317
+
318
+  // traverse all outputs and pixels
319
+  OutputMap::iterator out = m_outputMap.begin();
320
+  OutputMap::iterator outend = m_outputMap.end();
321
+  unsigned long o = 0;
322
+  unsigned long oi = 0;
323
+  for (; out != outend; ++out, ++o, oi += pixels * 3) {
324
+    Output &output = out->second;
325
+    std::vector<Pixel>::iterator pix = output.m_pixels.begin();
326
+    std::vector<Pixel>::iterator pixend = output.m_pixels.end();
327
+    unsigned long p = 0;
328
+    unsigned long pi = oi;
329
+    for (; pix != pixend; ++pix, ++p, pi += 3) {
330
+      Pixel &pixel = *pix;
331
+
332
+      // default color: black (in case pixel is not in pixel data)
333
+      uint8_t r = 0, g = 0, b = 0;
334
+      // get color of pixel from data
335
+      if (o < outputs && p < pixels) {
336
+        r = mapRed  .display2video(data[pi + 0]);
337
+        g = mapGreen.display2video(data[pi + 1]);
338
+        b = mapBlue .display2video(data[pi + 2]);
339
+      }
340
+      // store color
341
+      pixel.setColor(r, g, b);
342
+    } // for pix, p
343
+  } // for out, o
344
+
345
+  // count packet
346
+  ++m_packets;
347
+}
348
+
... ...
@@ -23,6 +23,7 @@
23 23
 #include <arpa/inet.h>
24 24
 #include <gtkmm.h>
25 25
 #include <map>
26
+#include <stdint.h>
26 27
 #include <vector>
27 28
 
28 29
 #include "bbox.h"
... ...
@@ -37,6 +38,9 @@ public:
37 38
   /// constructor
38 39
   Distri();
39 40
 
41
+  /// destructor
42
+  ~Distri();
43
+
40 44
   /**
41 45
    * @brief initialize
42 46
    * @param[in] distno distributor number
... ...
@@ -97,6 +101,20 @@ public:
97 101
    */
98 102
   void updateBBox(BBox &bb) const;
99 103
 
104
+  /**
105
+   * @brief create socket and listen for incoming packets
106
+   * @throws std::exception in case of error
107
+   */
108
+  void start();
109
+
110
+  /**
111
+   * @brief stop listening for incoming packets and close socket
112
+   */
113
+  void stop();
114
+
115
+  /// get and reset packet conter
116
+  unsigned long getPacketCounterAndReset();
117
+
100 118
   /**
101 119
    * @brief draw pixels of this distributor
102 120
    * @param[in] cairo cairo context for drawing
... ...
@@ -104,6 +122,21 @@ public:
104 122
    */
105 123
   void draw(Cairo::RefPtr<Cairo::Context> &cairo, Transform const &tf) const;
106 124
 
125
+protected:
126
+  /**
127
+   * @brief callback on packet input on socket
128
+   */
129
+  bool on_packet(Glib::IOCondition condition);
130
+
131
+  /**
132
+   * @brief process pixel data
133
+   * @param[in] outputs number of outputs in pixel data
134
+   * @param[in] pixels number of pixels per output in pixel data
135
+   * @param[in] data pixel data
136
+   */
137
+  void procPixelData(unsigned long outputs, unsigned long pixels,
138
+                     uint8_t const *data);
139
+
107 140
 protected:
108 141
   /// output data
109 142
   struct Output {
... ...
@@ -126,6 +159,14 @@ protected:
126 159
   Mapping m_mappings[Mapping::ChannelCount];
127 160
   /// outputs
128 161
   OutputMap m_outputMap;
162
+
163
+  /// IPv4/UDP socket
164
+  int m_sock;
165
+  /// Glib connection for notification of input on m_sock
166
+  sigc::connection m_conn;
167
+
168
+  /// received packets since last reset
169
+  unsigned long m_packets;
129 170
 };
130 171
 
131 172
 #endif // #ifndef DISTRI_H
... ...
@@ -34,9 +34,7 @@ Draw::Draw(BaseObjectType *cobject,
34 34
   m_builder(builder),
35 35
   m_config(NULL)
36 36
 {
37
-  // initialize image
38
-  updateImage();
39
-  queue_draw();
37
+  reqUpdate();
40 38
 }
41 39
 
42 40
 /// virtual destructor
... ...
@@ -49,7 +47,12 @@ void Draw::setConfig(Config const&config)
49 47
 {
50 48
   m_config = &config;
51 49
 
52
-  // re-initialize image
50
+  reqUpdate();
51
+}
52
+
53
+/// request image update
54
+void Draw::reqUpdate()
55
+{
53 56
   updateImage();
54 57
   queue_draw();
55 58
 }
... ...
@@ -61,9 +64,7 @@ void Draw::setConfig(Config const&config)
61 64
  */
62 65
 bool Draw::on_configure_event(GdkEventConfigure *cfg)
63 66
 {
64
-  // update image
65
-  updateImage();
66
-  queue_draw();
67
+  reqUpdate();
67 68
 
68 69
   return true;
69 70
   (void)cfg;
... ...
@@ -110,7 +111,6 @@ void Draw::updateImage()
110 111
   Transform tf(m_config->getBBox(), width, height);
111 112
 
112 113
   // draw pixels of all distributors
113
-  cairo->set_source_rgb(1.0, 0.0, 0.0); // FIXME
114 114
   m_config->draw(cairo, tf);
115 115
 }
116 116
 
... ...
@@ -42,6 +42,9 @@ public:
42 42
   /// set configuration object
43 43
   void setConfig(Config const&config);
44 44
 
45
+  /// request image update
46
+  void reqUpdate();
47
+
45 48
 protected:
46 49
   /**
47 50
    * @brief widget is (re)configured
... ...
@@ -54,9 +54,15 @@ int main(int argc, char *argv[])
54 54
     // append config file name to window title
55 55
     mainWindow->set_title(mainWindow->get_title() + " - " + configFile);
56 56
 
57
+    // start operation
58
+    config.start();
59
+
57 60
     // run GTK main loop
58 61
     Gtk::Main::run();
59 62
 
63
+    // stop operation
64
+    config.stop();
65
+
60 66
   } catch (std::exception &ex) {
61 67
     std::cerr << ex.what() << std::endl;
62 68
     return 3;
... ...
@@ -23,6 +23,8 @@
23 23
 #include "draw.h"
24 24
 #include "main_window.h"
25 25
 
26
+#define TIMER_MS (40)
27
+
26 28
 /**
27 29
  * @brief constructor
28 30
  * @param[in] Gtk Window object (a C object)
... ...
@@ -32,7 +34,9 @@ MainWindow::MainWindow(BaseObjectType *cobject,
32 34
                        const Glib::RefPtr<Gtk::Builder> &builder):
33 35
   Gtk::Window(cobject),
34 36
   m_builder(builder),
35
-  m_config(NULL)
37
+  m_config(NULL),
38
+  m_packets(0),
39
+  m_time_ms(0)
36 40
 {
37 41
   // get widgets
38 42
   builder->get_widget_derived("Draw", m_draw);
... ...
@@ -40,15 +44,22 @@ MainWindow::MainWindow(BaseObjectType *cobject,
40 44
 
41 45
   // connect callbacks
42 46
   signal_hide().connect(sigc::mem_fun(*this, &MainWindow::on_hide));
47
+
48
+  // start timer
49
+  sigc::slot<bool> timer_slot = sigc::bind(
50
+      sigc::mem_fun(*this, &MainWindow::on_timer), 0);
51
+  m_timer = Glib::signal_timeout().connect(timer_slot, TIMER_MS);
43 52
 }
44 53
 
45 54
 /// virtual destructor
46 55
 MainWindow::~MainWindow()
47 56
 {
57
+  // stop timer
58
+  m_timer.disconnect();
48 59
 }
49 60
 
50 61
 /// set configuration object
51
-void MainWindow::setConfig(Config const &config)
62
+void MainWindow::setConfig(Config &config)
52 63
 {
53 64
   m_config = &config;
54 65
   m_draw->setConfig(config);
... ...
@@ -68,3 +79,33 @@ void MainWindow::set_status(std::string const &txt)
68 79
   m_status->push(txt);
69 80
 }
70 81
 
82
+/// timer callback
83
+bool MainWindow::on_timer(int timerno)
84
+{
85
+  // get packet counter
86
+  unsigned long pack = 0;
87
+  if (m_config) {
88
+    pack = m_config->getPacketCounterAndReset();
89
+  }
90
+
91
+  // request redraw if a packet has been received
92
+  if (pack > 0) {
93
+    m_draw->reqUpdate();
94
+  }
95
+
96
+  // count packets per second and update status
97
+  m_packets += pack;
98
+  m_time_ms += TIMER_MS;
99
+  if (m_time_ms >= 1000) {
100
+    std::stringstream msg;
101
+    msg << "receiving " << m_packets << " packets per second";
102
+    set_status(msg.str());
103
+    m_packets = 0;
104
+    m_time_ms = 0;
105
+  }
106
+
107
+  // keep callback registered
108
+  return true;
109
+  (void)timerno;
110
+}
111
+
... ...
@@ -41,7 +41,7 @@ public:
41 41
   virtual ~MainWindow();
42 42
 
43 43
   /// set configuration object
44
-  void setConfig(Config const& config);
44
+  void setConfig(Config & config);
45 45
 
46 46
 protected:
47 47
   /// window is being hidden
... ...
@@ -50,6 +50,9 @@ protected:
50 50
   /// set status bar text
51 51
   void set_status(std::string const &txt);
52 52
 
53
+  /// timer callback
54
+  bool on_timer(int timerno);
55
+
53 56
 protected:
54 57
   /// reference to GTK builder object
55 58
   Glib::RefPtr<Gtk::Builder> m_builder;
... ...
@@ -60,7 +63,17 @@ protected:
60 63
   Gtk::Statusbar *m_status;
61 64
 
62 65
   /// configuration object
63
-  Config const *m_config;
66
+  Config *m_config;
67
+
68
+  /// timer connection
69
+  sigc::connection m_timer;
70
+
71
+  /// packet count per second
72
+  //@{
73
+  unsigned long m_packets; ///< packet counter
74
+  unsigned long m_time_ms; ///< time counter
75
+  //@}
76
+
64 77
 };
65 78
 
66 79
 #endif // #ifndef MAIN_WINDOW_H
... ...
@@ -18,6 +18,7 @@
18 18
  */
19 19
 
20 20
 #include <gtkmm.h>
21
+#include <stdint.h>
21 22
 
22 23
 #include "bbox.h"
23 24
 #include "pixel.h"
... ...
@@ -27,7 +28,10 @@
27 28
 Pixel::Pixel():
28 29
   m_x(0.5),
29 30
   m_y(0.5),
30
-  m_r(0.0)
31
+  m_r(0.0),
32
+  m_red  (64),
33
+  m_green(64),
34
+  m_blue (64)
31 35
 {
32 36
 }
33 37
 
... ...
@@ -35,7 +39,10 @@ Pixel::Pixel():
35 39
 Pixel::Pixel(double x, double y, double r):
36 40
   m_x(x),
37 41
   m_y(y),
38
-  m_r(r)
42
+  m_r(r),
43
+  m_red  (64),
44
+  m_green(64),
45
+  m_blue (64)
39 46
 {
40 47
 }
41 48
 
... ...
@@ -61,6 +68,7 @@ void Pixel::draw(Cairo::RefPtr<Cairo::Context> &cairo,
61 68
   double x, y, r;
62 69
   tf.apply(m_x, m_y, x, y);
63 70
   tf.applyZoom(m_r, r);
71
+  cairo->set_source_rgb(m_red / 255.0, m_green / 255.0, m_blue / 255.0);
64 72
   cairo->arc(x, y, r, 0.0, 2.0 * M_PI);
65 73
   cairo->fill();
66 74
 }
... ...
@@ -21,6 +21,7 @@
21 21
 #define PIXEL_H
22 22
 
23 23
 #include <gtkmm.h>
24
+#include <stdint.h>
24 25
 
25 26
 #include "bbox.h"
26 27
 #include "transform.h"
... ...
@@ -41,6 +42,16 @@ public:
41 42
    */
42 43
   void updateBBox(BBox &bb) const;
43 44
 
45
+  /**
46
+   * @brief set color of pixel
47
+   */
48
+  void setColor(uint8_t red, uint8_t green, uint8_t blue)
49
+  {
50
+    m_red   = red  ;
51
+    m_green = green;
52
+    m_blue  = blue ;
53
+  }
54
+
44 55
   /**
45 56
    * @brief draw pixel
46 57
    * @param[in] cairo cairo context for drawing
... ...
@@ -55,6 +66,10 @@ protected:
55 66
    double m_y; ///< y coordinate
56 67
    double m_r; ///< radius
57 68
    //@}
69
+   /// color of pixel
70
+   //@{
71
+   uint8_t m_red, m_green, m_blue;
72
+   //@}
58 73
 };
59 74
 
60 75
 #endif // #ifndef PIXEL_H
61 76