Open Lighting Architecture  0.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ArtNetNode.h
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  * ArtNetNodeImpl.h
17  * Header file for the ArtNetNodeImpl class
18  * Copyright (C) 2005-2010 Simon Newton
19  */
20 
21 #ifndef PLUGINS_ARTNET_ARTNETNODE_H_
22 #define PLUGINS_ARTNET_ARTNETNODE_H_
23 
24 #include <map>
25 #include <memory>
26 #include <string>
27 #include <utility>
28 #include <vector>
29 
30 #include "ola/Callback.h"
31 #include "ola/Clock.h"
32 #include "ola/DmxBuffer.h"
34 #include "ola/network/Interface.h"
35 #include "ola/io/SelectServerInterface.h"
36 #include "ola/network/Socket.h"
38 #include "ola/rdm/RDMCommand.h"
40 #include "ola/rdm/UIDSet.h"
41 #include "ola/timecode/TimeCode.h"
42 #include "plugins/artnet/ArtNetPackets.h"
43 
44 namespace ola {
45 namespace plugin {
46 namespace artnet {
47 
48 
49 // The directions are the opposite from what OLA uses
50 typedef enum {
51  ARTNET_INPUT_PORT, // sends ArtNet data
52  ARTNET_OUTPUT_PORT, // receives ArtNet data
53 } artnet_port_type;
54 
55 typedef enum {
56  ARTNET_MERGE_HTP, // default
57  ARTNET_MERGE_LTP,
58 } artnet_merge_mode;
59 
60 
61 // This can be passed to SetPortUniverse to disable ports
62 static const uint8_t ARTNET_DISABLE_PORT = 0xf0;
63 
65  public:
67  : always_broadcast(false),
68  use_limited_broadcast_address(false),
69  rdm_queue_size(20),
70  broadcast_threshold(30),
71  input_port_count(4) {
72  }
73 
74  bool always_broadcast;
75  bool use_limited_broadcast_address;
76  unsigned int rdm_queue_size;
77  unsigned int broadcast_threshold;
78  uint8_t input_port_count;
79 };
80 
81 
83  public:
84  ArtNetNodeImpl(const ola::network::Interface &interface,
86  const ArtNetNodeOptions &options,
87  ola::network::UDPSocketInterface *socket = NULL);
88  virtual ~ArtNetNodeImpl();
89 
90  bool Start();
91  bool Stop();
92 
93  /*
94  * Configuration mode.
95  * This allows the caller to make changes without triggering an ArtPoll or
96  * ArtPollReply per change. e.g.
97  * node.EnterConfigurationMode()
98  * node.SetShortName()
99  * node.SetInputPortUniverse()
100  * node.SetOutputPortUniverse()
101  * // The poll / poll reply is sent here
102  * node.ExitConfigurationMode()
103  */
104  bool EnterConfigurationMode();
105  bool ExitConfigurationMode();
106 
107 
108  // Various parameters to control the behaviour
109  bool SetShortName(const std::string &name);
110  std::string ShortName() const { return m_short_name; }
111  bool SetLongName(const std::string &name);
112  std::string LongName() const { return m_long_name; }
113 
114  uint8_t NetAddress() const { return m_net_address; }
115  bool SetNetAddress(uint8_t net_address);
116 
117  bool SetSubnetAddress(uint8_t subnet_address);
118  uint8_t SubnetAddress() const {
119  return m_output_ports[0].universe_address >> 4;
120  }
121 
122  uint8_t InputPortCount() const;
123  bool SetInputPortUniverse(uint8_t port_id, uint8_t universe_id);
124  uint8_t GetInputPortUniverse(uint8_t port_id) const;
125  void DisableInputPort(uint8_t port_id);
126  bool InputPortState(uint8_t port_id) const;
127 
128  bool SetOutputPortUniverse(uint8_t port_id, uint8_t universe_id);
129  uint8_t GetOutputPortUniverse(uint8_t port_id);
130  void DisableOutputPort(uint8_t port_id);
131  bool OutputPortState(uint8_t port_id) const;
132 
133  void SetBroadcastThreshold(unsigned int threshold) {
134  m_broadcast_threshold = threshold;
135  }
136 
137  bool SetMergeMode(uint8_t port_id, artnet_merge_mode merge_mode);
138 
139  // Poll, this should be called periodically if we're sending data.
140  bool SendPoll();
141 
142  // The following apply to Input Ports (those which send data)
143  bool SendDMX(uint8_t port_id, const ola::DmxBuffer &buffer);
144  void RunFullDiscovery(uint8_t port_id,
146  void RunIncrementalDiscovery(uint8_t port_id,
148  void SendRDMRequest(uint8_t port_id,
149  const ola::rdm::RDMRequest *request,
150  ola::rdm::RDMCallback *on_complete);
151  bool SetUnsolicitedUIDSetHandler(
152  uint8_t port_id,
154  void GetSubscribedNodes(
155  uint8_t port_id,
156  std::vector<ola::network::IPV4Address> *node_addresses);
157 
158  // The following apply to Output Ports (those which receive data);
159  bool SetDMXHandler(uint8_t port_id,
160  DmxBuffer *buffer,
161  ola::Callback0<void> *handler);
162  bool SendTod(uint8_t port_id, const ola::rdm::UIDSet &uid_set);
163  bool SetOutputPortRDMHandlers(
164  uint8_t port_id,
165  ola::Callback0<void> *on_discover,
166  ola::Callback0<void> *on_flush,
167  ola::Callback2<void,
168  const ola::rdm::RDMRequest*,
169  ola::rdm::RDMCallback*> *on_rdm_request);
170 
171  // send time code
172  bool SendTimeCode(const ola::timecode::TimeCode &timecode);
173 
174  private:
175  class InputPort;
176  typedef std::vector<InputPort*> InputPorts;
177 
178  // map a uid to a IP address and the number of times we've missed a
179  // response.
180  typedef std::map<ola::rdm::UID,
181  std::pair<ola::network::IPV4Address, uint8_t> > uid_map;
182 
183  enum { MAX_MERGE_SOURCES = 2 };
184 
185  struct DMXSource {
186  DmxBuffer buffer;
187  TimeStamp timestamp;
189  };
190 
191  // Output Ports receive ArtNet data
192  struct OutputPort {
193  uint8_t universe_address;
194  uint8_t sequence_number;
195  bool enabled;
196  artnet_merge_mode merge_mode;
197  bool is_merging;
198  DMXSource sources[MAX_MERGE_SOURCES];
199  DmxBuffer *buffer;
200  std::map<ola::rdm::UID, ola::network::IPV4Address> uid_map;
201  Callback0<void> *on_data;
202  Callback0<void> *on_discover;
203  Callback0<void> *on_flush;
204  ola::Callback2<void,
205  const ola::rdm::RDMRequest*,
206  ola::rdm::RDMCallback*> *on_rdm_request;
207  };
208 
209  bool m_running;
210  uint8_t m_net_address; // this is the 'net' portion of the Artnet address
211  bool m_send_reply_on_change;
212  std::string m_short_name;
213  std::string m_long_name;
214  unsigned int m_broadcast_threshold;
215  unsigned int m_unsolicited_replies;
217  bool m_always_broadcast;
218  bool m_use_limited_broadcast_address;
219 
220  // The following keep track of "Configuration mode"
221  bool m_in_configuration_mode;
222  bool m_artpoll_required;
223  bool m_artpollreply_required;
224 
225  InputPorts m_input_ports;
226  OutputPort m_output_ports[ARTNET_MAX_PORTS];
227  ola::network::Interface m_interface;
228  std::auto_ptr<ola::network::UDPSocketInterface> m_socket;
229 
231  ArtNetNodeImpl& operator=(const ArtNetNodeImpl&);
232 
233  void SocketReady();
234  bool SendPollIfAllowed();
235  bool SendPollReplyIfRequired();
236  bool SendPollReply(const ola::network::IPV4Address &destination);
237  bool SendIPReply(const ola::network::IPV4Address &destination);
238  void HandlePacket(const ola::network::IPV4Address &source_address,
239  const artnet_packet &packet,
240  unsigned int packet_size);
241  void HandlePollPacket(const ola::network::IPV4Address &source_address,
242  const artnet_poll_t &packet,
243  unsigned int packet_size);
244  void HandleReplyPacket(const ola::network::IPV4Address &source_address,
245  const artnet_reply_t &packet,
246  unsigned int packet_size);
247  void HandleDataPacket(const ola::network::IPV4Address &source_address,
248  const artnet_dmx_t &packet,
249  unsigned int packet_size);
250  void HandleTodRequest(const ola::network::IPV4Address &source_address,
251  const artnet_todrequest_t &packet,
252  unsigned int packet_size);
253  void HandleTodData(const ola::network::IPV4Address &source_address,
254  const artnet_toddata_t &packet,
255  unsigned int packet_size);
256  void HandleTodControl(const ola::network::IPV4Address &source_address,
257  const artnet_todcontrol_t &packet,
258  unsigned int packet_size);
259  void HandleRdm(const ola::network::IPV4Address &source_address,
260  const artnet_rdm_t &packet,
261  unsigned int packet_size);
262  void RDMRequestCompletion(ola::network::IPV4Address destination,
263  uint8_t port_id,
264  uint8_t universe_address,
265  ola::rdm::rdm_response_code code,
266  const ola::rdm::RDMResponse *response,
267  const std::vector<std::string> &packets);
268  void HandleRDMResponse(InputPort *port,
269  const std::string &rdm_data,
270  const ola::network::IPV4Address &source_address);
271  void HandleIPProgram(const ola::network::IPV4Address &source_address,
272  const artnet_ip_prog_t &packet,
273  unsigned int packet_size);
274  void PopulatePacketHeader(artnet_packet *packet, uint16_t op_code);
275  bool SendPacket(const artnet_packet &packet,
276  unsigned int size,
277  const ola::network::IPV4Address &destination);
278  void TimeoutRDMRequest(InputPort *port);
279  bool SendRDMCommand(const ola::rdm::RDMCommand &command,
280  const ola::network::IPV4Address &destination,
281  uint8_t universe);
282  void UpdatePortFromSource(OutputPort *port, const DMXSource &source);
283  bool CheckPacketVersion(const ola::network::IPV4Address &source_address,
284  const std::string &packet_type,
285  uint16_t version);
286  bool CheckPacketSize(const ola::network::IPV4Address &source_address,
287  const std::string &packet_type,
288  unsigned int actual_size,
289  unsigned int expected_size);
290 
291  // methods for accessing Input & Output ports
292  InputPort *GetInputPort(uint8_t port_id, bool warn = true);
293  const InputPort *GetInputPort(uint8_t port_id) const;
294  InputPort *GetEnabledInputPort(uint8_t port_id, const std::string &action);
295 
296  OutputPort *GetOutputPort(uint8_t port_id);
297  const OutputPort *GetOutputPort(uint8_t port_id) const;
298  OutputPort *GetEnabledOutputPort(uint8_t port_id, const std::string &action);
299 
300  void UpdatePortFromTodPacket(InputPort *port,
301  const ola::network::IPV4Address &source_address,
302  const artnet_toddata_t &packet,
303  unsigned int packet_size);
304 
305  void ReleaseDiscoveryLock(InputPort *port);
306  bool StartDiscoveryProcess(InputPort *port,
308 
309  bool InitNetwork();
310 
311  static const char ARTNET_ID[];
312  static const uint16_t ARTNET_PORT = 6454;
313  static const uint16_t OEM_CODE = 0x0431;
314  static const uint16_t ARTNET_VERSION = 14;
315  // after not receiving a PollReply after this many seconds we declare the
316  // node as dead. This is set to 3x the POLL_INTERVAL in ArtNetDevice.
317  static const uint8_t NODE_CODE = 0x00;
318  static const uint16_t MAX_UIDS_PER_UNIVERSE = 0xffff;
319  static const uint8_t RDM_VERSION = 0x01; // v1.0 standard baby!
320  static const uint8_t TOD_FLUSH_COMMAND = 0x01;
321  static const unsigned int MERGE_TIMEOUT = 10; // As per the spec
322  // seconds after which a node is marked as inactive for the dmx merging
323  static const unsigned int NODE_TIMEOUT = 31;
324  // mseconds we wait for a TodData packet before declaring a node missing
325  static const unsigned int RDM_TOD_TIMEOUT_MS = 4000;
326  // Number of missed TODs before we decide a UID has gone
327  static const unsigned int RDM_MISSED_TODDATA_LIMIT = 3;
328  // The maximum number of requests we'll allow in the queue. This is a per
329  // port (universe) limit.
330  static const unsigned int RDM_REQUEST_QUEUE_LIMIT = 100;
331  // How long to wait for a response to an RDM Request
332  static const unsigned int RDM_REQUEST_TIMEOUT_MS = 2000;
333 };
334 
335 
342  public:
343  ArtNetNodeImplRDMWrapper(ArtNetNodeImpl *impl, uint8_t port_id):
344  m_impl(impl),
345  m_port_id(port_id) {
346  }
348 
350  ola::rdm::RDMCallback *on_complete) {
351  m_impl->SendRDMRequest(m_port_id, request, on_complete);
352  }
353 
355  m_impl->RunFullDiscovery(m_port_id, callback);
356  }
357 
358  void RunIncrementalDiscovery(ola::rdm::RDMDiscoveryCallback *callback) {
359  m_impl->RunIncrementalDiscovery(m_port_id, callback);
360  }
361 
362  private:
363  ArtNetNodeImpl *m_impl;
364  uint8_t m_port_id;
365 };
366 
367 
371 class ArtNetNode {
372  public:
373  ArtNetNode(const ola::network::Interface &interface,
375  const ArtNetNodeOptions &options,
376  ola::network::UDPSocketInterface *socket = NULL);
377  virtual ~ArtNetNode();
378 
379  bool Start() { return m_impl.Start(); }
380  bool Stop() { return m_impl.Stop(); }
381 
382  bool EnterConfigurationMode() {
383  return m_impl.EnterConfigurationMode();
384  }
385  bool ExitConfigurationMode() {
386  return m_impl.ExitConfigurationMode();
387  }
388 
389  // Various parameters to control the behaviour
390  bool SetShortName(const std::string &name) {
391  return m_impl.SetShortName(name);
392  }
393 
394  std::string ShortName() const { return m_impl.ShortName(); }
395  bool SetLongName(const std::string &name) {
396  return m_impl.SetLongName(name);
397  }
398 
399  std::string LongName() const { return m_impl.LongName(); }
400 
401  uint8_t NetAddress() const { return m_impl.NetAddress(); }
402  bool SetNetAddress(uint8_t net_address) {
403  return m_impl.SetNetAddress(net_address);
404  }
405  bool SetSubnetAddress(uint8_t subnet_address) {
406  return m_impl.SetSubnetAddress(subnet_address);
407  }
408  uint8_t SubnetAddress() const {
409  return m_impl.SubnetAddress();
410  }
411 
412  uint8_t InputPortCount() const {
413  return m_impl.InputPortCount();
414  }
415 
416  bool SetInputPortUniverse(uint8_t port_id, uint8_t universe_id) {
417  return m_impl.SetInputPortUniverse(port_id, universe_id);
418  }
419  uint8_t GetInputPortUniverse(uint8_t port_id) const {
420  return m_impl.GetInputPortUniverse(port_id);
421  }
422  void DisableInputPort(uint8_t port_id) {
423  m_impl.DisableInputPort(port_id);
424  }
425  bool InputPortState(uint8_t port_id) const {
426  return m_impl.InputPortState(port_id);
427  }
428 
429  bool SetOutputPortUniverse(uint8_t port_id, uint8_t universe_id) {
430  return m_impl.SetOutputPortUniverse(port_id, universe_id);
431  }
432  uint8_t GetOutputPortUniverse(uint8_t port_id) {
433  return m_impl.GetOutputPortUniverse(port_id);
434  }
435  void DisableOutputPort(uint8_t port_id) {
436  m_impl.DisableOutputPort(port_id);
437  }
438  bool OutputPortState(uint8_t port_id) const {
439  return m_impl.OutputPortState(port_id);
440  }
441 
442  void SetBroadcastThreshold(unsigned int threshold) {
443  m_impl.SetBroadcastThreshold(threshold);
444  }
445 
446  bool SetMergeMode(uint8_t port_id, artnet_merge_mode merge_mode) {
447  return m_impl.SetMergeMode(port_id, merge_mode);
448  }
449 
450  // Poll, this should be called periodically if we're sending data.
451  bool SendPoll() {
452  return m_impl.SendPoll();
453  }
454 
455  // The following apply to Input Ports (those which send data)
456  bool SendDMX(uint8_t port_id, const ola::DmxBuffer &buffer) {
457  return m_impl.SendDMX(port_id, buffer);
458  }
459  void RunFullDiscovery(uint8_t port_id,
461  void RunIncrementalDiscovery(uint8_t port_id,
463  void SendRDMRequest(uint8_t port_id,
464  const ola::rdm::RDMRequest *request,
465  ola::rdm::RDMCallback *on_complete);
466 
467  /*
468  * This handler is called if we receive ArtTod packets and a discovery
469  * process isn't running.
470  */
471  bool SetUnsolicitedUIDSetHandler(
472  uint8_t port_id,
474  return m_impl.SetUnsolicitedUIDSetHandler(port_id, on_tod);
475  }
476  void GetSubscribedNodes(
477  uint8_t port_id,
478  std::vector<ola::network::IPV4Address> *node_addresses) {
479  m_impl.GetSubscribedNodes(port_id, node_addresses);
480  }
481 
482  // The following apply to Output Ports (those which receive data);
483  bool SetDMXHandler(uint8_t port_id,
484  DmxBuffer *buffer,
485  ola::Callback0<void> *handler) {
486  return m_impl.SetDMXHandler(port_id, buffer, handler);
487  }
488  bool SendTod(uint8_t port_id, const ola::rdm::UIDSet &uid_set) {
489  return m_impl.SendTod(port_id, uid_set);
490  }
491  bool SetOutputPortRDMHandlers(
492  uint8_t port_id,
493  ola::Callback0<void> *on_discover,
494  ola::Callback0<void> *on_flush,
495  ola::Callback2<void,
496  const ola::rdm::RDMRequest*,
497  ola::rdm::RDMCallback*> *on_rdm_request) {
498  return m_impl.SetOutputPortRDMHandlers(port_id,
499  on_discover,
500  on_flush,
501  on_rdm_request);
502  }
503 
504  // Time Code methods
505  bool SendTimeCode(const ola::timecode::TimeCode &timecode) {
506  return m_impl.SendTimeCode(timecode);
507  }
508 
509  private:
510  ArtNetNodeImpl m_impl;
511  std::vector<ArtNetNodeImplRDMWrapper*> m_wrappers;
512  std::vector<ola::rdm::DiscoverableQueueingRDMController*> m_controllers;
513 
514  bool CheckInputPortId(uint8_t port_id);
515 };
516 } // namespace artnet
517 } // namespace plugin
518 } // namespace ola
519 #endif // PLUGINS_ARTNET_ARTNETNODE_H_