• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenLightingProject / ola / 24635061330

19 Apr 2026 05:36PM UTC coverage: 44.862% (-0.002%) from 44.864%
24635061330

Pull #2046

github

web-flow
Merge 8d08f158d into c6196f753
Pull Request #2046: Plugfest discovered fixes

8554 of 19846 branches covered (43.1%)

11 of 32 new or added lines in 7 files covered. (34.38%)

2 existing lines in 2 files now uncovered.

22105 of 49273 relevant lines covered (44.86%)

47.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.44
/plugins/espnet/EspNetNode.cpp
1
/*
2
 * This program is free software; you can redistribute it and/or modify
3
 * it under the terms of the GNU General Public License as published by
4
 * the Free Software Foundation; either version 2 of the License, or
5
 * (at your option) any later version.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU Library General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15
 *
16
 * EspNetNode.cpp
17
 * A EspNet node
18
 * Copyright (C) 2005 Simon Newton
19
 */
20

21
#include <string.h>
22
#include <algorithm>
23
#include <map>
24
#include <string>
25
#include "ola/Logging.h"
26
#include "ola/network/InterfacePicker.h"
27
#include "ola/network/IPV4Address.h"
28
#include "ola/network/MACAddress.h"
29
#include "ola/network/NetworkUtils.h"
30
#include "plugins/espnet/EspNetNode.h"
31

32

33
namespace ola {
34
namespace plugin {
35
namespace espnet {
36

37
using ola::network::HostToNetwork;
38
using ola::network::IPV4Address;
39
using ola::network::IPV4SocketAddress;
40
using ola::network::NetworkToHost;
41
using ola::network::UDPSocket;
42
using ola::Callback0;
43
using std::map;
44
using std::string;
45

46
const char EspNetNode::NODE_NAME[] = "OLA Node";
47

48
/*
49
 * Create a new node
50
 * @param ip_address the IP address to prefer to listen on, if NULL we choose
51
 * one.
52
 */
53
EspNetNode::EspNetNode(const string &ip_address)
1✔
54
    : m_running(false),
1✔
55
      m_options(DEFAULT_OPTIONS),
1✔
56
      m_tos(DEFAULT_TOS),
1✔
57
      m_ttl(DEFAULT_TTL),
1✔
58
      m_universe(0),
1✔
59
      m_type(ESPNET_NODE_TYPE_IO),
1✔
60
      m_node_name(NODE_NAME),
2✔
61
      m_preferred_ip(ip_address) {
1✔
62
}
1✔
63

64

65
/*
66
 * Cleanup
67
 */
68
EspNetNode::~EspNetNode() {
1✔
69
  Stop();
1✔
70

71
  std::map<uint8_t, universe_handler>::iterator iter;
1✔
72
  for (iter = m_handlers.begin(); iter != m_handlers.end(); ++iter) {
1✔
73
    delete iter->second.closure;
×
74
  }
75
  m_handlers.clear();
1✔
76
}
1✔
77

78

79
/*
80
 * Start this node
81
 */
82
bool EspNetNode::Start() {
1✔
83
  if (m_running) {
1✔
84
    return false;
85
  }
86

87
  ola::network::InterfacePicker *picker =
1✔
88
    ola::network::InterfacePicker::NewPicker();
1✔
89

90
  if (!picker->ChooseInterface(&m_interface, m_preferred_ip)) {
1✔
91
    OLA_INFO << "Failed to find an interface";
×
92
    delete picker;
×
93
    return false;
×
94
  }
95
  delete picker;
1✔
96

97
  if (!InitNetwork()) {
1✔
98
    return false;
99
  }
100

101
  m_running = true;
1✔
102
  return true;
1✔
103
}
104

105

106
/*
107
 * Stop this node
108
 */
109
bool EspNetNode::Stop() {
2✔
110
  if (!m_running) {
2✔
111
    return false;
112
  }
113

114
  m_running = false;
1✔
115
  return true;
1✔
116
}
117

118

119
/*
120
 * Called when there is data on this socket
121
 */
122
void EspNetNode::SocketReady() {
×
123
  espnet_packet_union_t packet;
×
124
  memset(&packet, 0, sizeof(packet));
×
125
  ola::network::IPV4SocketAddress source;
×
126

127
  ssize_t packet_size = sizeof(packet);
×
128
  if (!m_socket.RecvFrom(reinterpret_cast<uint8_t*>(&packet),
×
129
                         &packet_size,
130
                         &source)) {
131
    return;
132
  }
133

134
  if (packet_size < (ssize_t) sizeof(packet.poll.head)) {
×
135
    OLA_WARN << "Small espnet packet received, discarding";
×
136
    return;
×
137
  }
138

139
  // skip packets sent by us
140
  if (source.Host() == m_interface.ip_address) {
×
141
    return;
142
  }
143

144
  switch (NetworkToHost(packet.poll.head)) {
×
145
    case ESPNET_POLL:
×
146
      HandlePoll(packet.poll, packet_size, source.Host());
×
147
      break;
148
    case ESPNET_REPLY:
×
149
      HandleReply(packet.reply, packet_size, source.Host());
×
150
      break;
151
    case ESPNET_DMX:
×
152
      HandleData(packet.dmx, packet_size, source.Host());
×
153
      break;
154
    case ESPNET_ACK:
×
155
      HandleAck(packet.ack, packet_size, source.Host());
×
156
      break;
157
    default:
×
158
      OLA_INFO << "Skipping a packet with invalid header" << packet.poll.head;
×
159
  }
160
}
×
161

162

163
/*
164
 * Set the closure to be called when we receive data for this universe.
165
 * @param universe the universe to register the handler for
166
 * @param handler the Callback0 to call when there is data for this universe.
167
 * Ownership of the closure is transferred to the node.
168
 */
169
bool EspNetNode::SetHandler(uint8_t universe,
×
170
                            DmxBuffer *buffer,
171
                            Callback0<void> *closure) {
172
  if (!closure) {
×
173
    return false;
174
  }
175

176
  map<uint8_t, universe_handler>::iterator iter =
×
177
    m_handlers.find(universe);
×
178

179
  if (iter == m_handlers.end()) {
×
180
    universe_handler handler;
×
181
    handler.buffer = buffer;
×
182
    handler.closure = closure;
×
183
    m_handlers[universe] = handler;
×
184
  } else {
185
    Callback0<void> *old_closure = iter->second.closure;
×
186
    iter->second.closure = closure;
×
187
    delete old_closure;
×
188
  }
189
  return true;
190
}
191

192

193
/*
194
 * Remove the handler for this universe
195
 * @param universe the universe handler to remove
196
 * @param true if removed, false if it didn't exist
197
 */
198
bool EspNetNode::RemoveHandler(uint8_t universe) {
×
199
  map<uint8_t, universe_handler>::iterator iter =
×
200
      m_handlers.find(universe);
×
201

202
  if (iter != m_handlers.end()) {
×
203
    Callback0<void> *old_closure = iter->second.closure;
×
204
    m_handlers.erase(iter);
×
205
    delete old_closure;
×
206
    return true;
×
207
  }
208
  return false;
209
}
210

211

212
/*
213
 * Send an Esp Poll
214
 * @param full_poll
215
 */
216
bool EspNetNode::SendPoll(bool full_poll) {
×
217
  if (!m_running) {
×
218
    return false;
219
  }
220

221
  return SendEspPoll(m_interface.bcast_address, full_poll);
×
222
}
223

224

225
/*
226
 * Send some DMX data
227
 * @param universe the id of the universe to send
228
 * @param buffer the DMX data
229
 * @return true if it was sent successfully, false otherwise
230
 */
231
bool EspNetNode::SendDMX(uint8_t universe, const ola::DmxBuffer &buffer) {
×
232
  if (!m_running) {
×
233
    return false;
234
  }
235

236
  return SendEspData(m_interface.bcast_address, universe, buffer);
×
237
}
238

239

240
/*
241
 * Setup the networking components.
242
 */
243
bool EspNetNode::InitNetwork() {
1✔
244
  if (!m_socket.Init()) {
1✔
245
    OLA_WARN << "Socket init failed";
×
246
    return false;
×
247
  }
248

249
  if (!m_socket.Bind(IPV4SocketAddress(IPV4Address::WildCard(), ESPNET_PORT))) {
1✔
250
    return false;
251
  }
252

253
  if (!m_socket.EnableBroadcast()) {
1✔
254
    OLA_WARN << "Failed to enable broadcasting";
×
255
    return false;
×
256
  }
257

258
  m_socket.SetOnData(NewCallback(this, &EspNetNode::SocketReady));
1✔
259
  return true;
1✔
260
}
261

262

263
/*
264
 * Handle an Esp Poll packet
265
 */
266
void EspNetNode::HandlePoll(const espnet_poll_t &poll,
×
267
                            ssize_t length,
268
                            const IPV4Address &source) {
269
  OLA_DEBUG << "Got ESP Poll " << poll.type;
×
270
  if (length < (ssize_t) sizeof(espnet_poll_t)) {
×
271
    OLA_DEBUG << "Poll size too small " << length << " < "
×
272
              << sizeof(espnet_poll_t);
×
273
    return;
×
274
  }
275

276
  if (poll.type) {
×
277
    SendEspPollReply(source);
×
278
  } else {
279
    SendEspAck(source, 0, 0);
×
280
  }
281
}
282

283

284
/*
285
 * Handle an Esp reply packet. This does nothing at the moment.
286
 */
287
void EspNetNode::HandleReply(OLA_UNUSED const espnet_poll_reply_t &reply,
×
288
                             ssize_t length,
289
                             OLA_UNUSED const IPV4Address &source) {
290
  if (length < (ssize_t) sizeof(espnet_poll_reply_t)) {
×
291
    OLA_DEBUG << "Poll reply size too small " << length << " < "
×
292
              << sizeof(espnet_poll_reply_t);
×
293
    return;
×
294
  }
295
}
296

297

298
/*
299
 * Handle a Esp Ack packet
300
 */
301
void EspNetNode::HandleAck(OLA_UNUSED const espnet_ack_t &ack,
×
302
                           ssize_t length,
303
                           OLA_UNUSED const IPV4Address &source) {
304
  if (length < (ssize_t) sizeof(espnet_ack_t)) {
×
305
    OLA_DEBUG << "Ack size too small " << length << " < " <<
×
306
      sizeof(espnet_ack_t);
×
307
    return;
×
308
  }
309
}
310

311

312
/*
313
 * Handle an Esp data packet
314
 */
315
void EspNetNode::HandleData(const espnet_data_t &data,
×
316
                            ssize_t length,
317
                            OLA_UNUSED const IPV4Address &source) {
318
  static const ssize_t header_size = sizeof(espnet_data_t) - DMX_UNIVERSE_SIZE;
×
319
  if (length < header_size) {
×
320
    OLA_DEBUG << "Data size too small " << length << " < " << header_size;
×
321
    return;
×
322
  }
323

324
  map<uint8_t, universe_handler>::iterator iter =
×
325
      m_handlers.find(data.universe);
×
326

327
  if (iter == m_handlers.end()) {
×
328
    OLA_DEBUG << "Not interested in universe " <<
×
329
      static_cast<int>(data.universe) << ", skipping ";
×
330
    return;
×
331
  }
332

333
  ssize_t data_size = std::min(length - header_size,
×
334
                               (ssize_t) NetworkToHost(data.size));
×
335

336
  // we ignore the start code
337
  switch (data.type) {
×
338
    case DATA_RAW:
×
339
      iter->second.buffer->Set(data.data, data_size);
×
340
      break;
341
    case DATA_PAIRS:
×
342
      OLA_WARN << "espnet data pairs aren't supported";
×
343
      return;
×
344
    case DATA_RLE:
×
345
      m_decoder.Decode(iter->second.buffer, data.data, data_size);
×
346
      break;
347
    default:
×
348
      OLA_WARN << "unknown espnet data type " << data.type;
×
349
      return;
×
350
  }
351
  iter->second.closure->Run();
×
352
}
353

354

355
/*
356
 * Send an EspNet poll
357
 */
358
bool EspNetNode::SendEspPoll(const IPV4Address &dst, bool full) {
×
359
  espnet_packet_union_t packet;
×
NEW
360
  packet.poll.head = HostToNetwork(static_cast<uint32_t>(ESPNET_POLL));
×
361
  packet.poll.type = full;
×
362
  return SendPacket(dst, packet, sizeof(packet.poll));
×
363
}
364

365

366
/*
367
 * Send an EspNet Ack
368
 */
369
bool EspNetNode::SendEspAck(const IPV4Address &dst,
×
370
                            uint8_t status,
371
                            uint8_t crc) {
372
  espnet_packet_union_t packet;
×
NEW
373
  packet.ack.head = HostToNetwork(static_cast<uint32_t>(ESPNET_ACK));
×
374
  packet.ack.status = status;
×
375
  packet.ack.crc = crc;
×
376
  return SendPacket(dst, packet, sizeof(packet.ack));
×
377
}
378

379

380
/*
381
 * Send an EspNet Poll Reply
382
 */
383
bool EspNetNode::SendEspPollReply(const IPV4Address &dst) {
×
384
  espnet_packet_union_t packet;
×
NEW
385
  packet.reply.head = HostToNetwork(static_cast<uint32_t>(ESPNET_REPLY));
×
386

387
  m_interface.hw_address.Get(packet.reply.mac);
×
NEW
388
  packet.reply.type = HostToNetwork(static_cast<uint32_t>(m_type));
×
389
  packet.reply.version = FIRMWARE_VERSION;
×
390
  packet.reply.sw = SWITCH_SETTINGS;
×
391
  memcpy(packet.reply.name, m_node_name.data(), ESPNET_NAME_LENGTH);
×
392
  packet.reply.name[ESPNET_NAME_LENGTH - 1] = 0;
×
393

394
  packet.reply.option = m_options;
×
395
  packet.reply.option |= 0x01;  // We're always configured
×
396
  packet.reply.tos = m_tos;
×
397
  packet.reply.ttl = m_ttl;
×
398
  packet.reply.config.listen = 0x04;
×
399
  m_interface.ip_address.Get(packet.reply.config.ip);
×
400
  packet.reply.config.universe = m_universe;
×
401
  return SendPacket(dst, packet, sizeof(packet.reply));
×
402
}
403

404

405
/*
406
 * Send an EspNet data packet
407
 */
408
bool EspNetNode::SendEspData(const IPV4Address &dst,
×
409
                             uint8_t universe,
410
                             const DmxBuffer &buffer) {
411
  espnet_packet_union_t packet;
×
412
  memset(&packet.dmx, 0, sizeof(packet.dmx));
×
NEW
413
  packet.dmx.head = HostToNetwork(static_cast<uint32_t>(ESPNET_DMX));
×
414
  packet.dmx.universe = universe;
×
415
  packet.dmx.start = START_CODE;
×
416
  packet.dmx.type = DATA_RAW;
×
417
  unsigned int size = DMX_UNIVERSE_SIZE;
×
418
  buffer.Get(packet.dmx.data, &size);
×
NEW
419
  packet.dmx.size = HostToNetwork(static_cast<uint16_t>(size));
×
420
  return SendPacket(dst, packet, sizeof(packet.dmx));
×
421
}
422

423

424
/*
425
 * Send an EspNet packet
426
 */
427
bool EspNetNode::SendPacket(const IPV4Address &dst,
×
428
                            const espnet_packet_union_t &packet,
429
                            unsigned int size) {
430
  ssize_t bytes_sent = m_socket.SendTo(
×
431
      reinterpret_cast<const uint8_t*>(&packet),
432
      size,
433
      IPV4SocketAddress(dst, ESPNET_PORT));
×
434
  if (bytes_sent != (ssize_t) size) {
×
435
    OLA_WARN << "Only sent " << bytes_sent << " of " << size;
×
436
    return false;
×
437
  }
438
  return true;
439
}
440
}  // namespace espnet
441
}  // namespace plugin
442
}  // namespace ola
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc