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