syndilights/open-lighting-architecture/ola-0.8.4/plugins/shownet/ShowNetNode.cpp

361 lines
9.4 KiB
C++

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* ShowNetNode.cpp
* A ShowNet node
* Copyright (C) 2005-2009 Simon Newton
*/
#include <string.h>
#include <algorithm>
#include <map>
#include <string>
#include "ola/Logging.h"
#include "ola/network/NetworkUtils.h"
#include "plugins/shownet/ShowNetNode.h"
namespace ola {
namespace plugin {
namespace shownet {
using std::string;
using std::map;
using ola::network::UdpSocket;
using ola::network::HostToNetwork;
using ola::Closure;
/*
* Create a new node
* @param ip_address the IP address to prefer to listen on, if NULL we choose
* one.
*/
ShowNetNode::ShowNetNode(const string &ip_address)
: m_running(false),
m_packet_count(0),
m_node_name(),
m_preferred_ip(ip_address) {
memset(&m_destination, 0, sizeof(m_destination));
}
/*
* Cleanup
*/
ShowNetNode::~ShowNetNode() {
Stop();
std::map<unsigned int, universe_handler>::iterator iter;
for (iter = m_handlers.begin(); iter != m_handlers.end(); ++iter) {
delete iter->second.closure;
}
m_handlers.clear();
}
/*
* Start this node
*/
bool ShowNetNode::Start() {
if (m_running)
return false;
ola::network::InterfacePicker *picker =
ola::network::InterfacePicker::NewPicker();
if (!picker->ChooseInterface(&m_interface, m_preferred_ip)) {
delete picker;
OLA_INFO << "Failed to find an interface";
return false;
}
delete picker;
if (!InitNetwork())
return false;
m_destination.sin_family = AF_INET;
m_destination.sin_port = HostToNetwork(SHOWNET_PORT);
m_destination.sin_addr = m_interface.bcast_address;
m_running = true;
return true;
}
/*
* Stop this node
*/
bool ShowNetNode::Stop() {
if (!m_running)
return false;
if (m_socket) {
delete m_socket;
m_socket = NULL;
}
m_running = false;
return true;
}
/*
* Set the node name
* @param name the new node name
*/
void ShowNetNode::SetName(const string &name) {
m_node_name = name;
}
/*
* Send some DMX data
* @param universe the id of the universe to send
* @param buffer the DMX data
* @return true if it was send successfully, false otherwise
*/
bool ShowNetNode::SendDMX(unsigned int universe,
const ola::DmxBuffer &buffer) {
if (!m_running)
return false;
if (universe >= SHOWNET_MAX_UNIVERSES) {
OLA_WARN << "Universe index out of bounds, should be between 0 and" <<
SHOWNET_MAX_UNIVERSES << "), was " << universe;
return false;
}
shownet_data_packet packet;
unsigned int size = PopulatePacket(&packet, universe, buffer);
unsigned int bytes_sent = m_socket->SendTo(
reinterpret_cast<uint8_t*>(&packet),
size,
m_destination);
if (bytes_sent != size) {
OLA_WARN << "Only sent " << bytes_sent << " of " << size;
return false;
}
m_packet_count++;
return true;
}
/*
* Set the closure to be called when we receive data for this universe.
* @param universe the universe to register the handler for
* @param handler the Closure to call when there is data for this universe.
* Ownership of the closure is transferred to the node.
*/
bool ShowNetNode::SetHandler(unsigned int universe,
DmxBuffer *buffer,
Closure<void> *closure) {
if (!closure)
return false;
map<unsigned int, universe_handler>::iterator iter =
m_handlers.find(universe);
if (iter == m_handlers.end()) {
universe_handler handler;
handler.buffer = buffer;
handler.closure = closure;
m_handlers[universe] = handler;
} else {
Closure<void> *old_closure = iter->second.closure;
iter->second.closure = closure;
delete old_closure;
}
return true;
}
/*
* Remove the handler for this universe
* @param universe the universe handler to remove
* @param true if removed, false if it didn't exist
*/
bool ShowNetNode::RemoveHandler(unsigned int universe) {
map<unsigned int, universe_handler>::iterator iter =
m_handlers.find(universe);
if (iter != m_handlers.end()) {
Closure<void> *old_closure = iter->second.closure;
m_handlers.erase(iter);
delete old_closure;
return true;
}
return false;
}
/*
* Called when there is data on this socket
*/
void ShowNetNode::SocketReady() {
shownet_data_packet packet;
ssize_t packet_size = sizeof(packet);
struct sockaddr_in source;
socklen_t source_length = sizeof(source);
if (!m_socket->RecvFrom(reinterpret_cast<uint8_t*>(&packet),
&packet_size,
source,
source_length))
return;
// skip packets sent by us
if (source.sin_addr.s_addr != m_interface.ip_address.s_addr)
HandlePacket(packet, packet_size);
}
/*
* Handle a shownet packet
*/
bool ShowNetNode::HandlePacket(const shownet_data_packet &packet,
unsigned int packet_size) {
unsigned int header_size = sizeof(packet) - sizeof(packet.data);
if (packet_size <= header_size) {
OLA_WARN << "Skipping small shownet packet received, size=" << packet_size;
return false;
}
if (packet.sigHi != SHOWNET_ID_HIGH || packet.sigLo != SHOWNET_ID_LOW) {
OLA_INFO << "Skipping a packet that isn't shownet";
return false;
}
if (packet.indexBlock[0] < MAGIC_INDEX_OFFSET) {
OLA_WARN << "Strange ShowNet packet, indexBlock[0] is " <<
packet.indexBlock[0] << ", please contact the developers!";
return false;
}
// We only handle data from the first slot
// enc_length is the size of the received (optionally encoded) DMX data
int enc_len = packet.indexBlock[1] - packet.indexBlock[0];
if (enc_len < 1 || packet.netSlot[0] == 0) {
OLA_WARN << "Invalid shownet packet, enc_len=" << enc_len << ", netSlot="
<< packet.netSlot[0];
return false;
}
// the offset into packet.data of the actual data
unsigned int data_offset = packet.indexBlock[0] - MAGIC_INDEX_OFFSET;
unsigned int received_data_size = packet_size - header_size;
if (data_offset + enc_len > received_data_size) {
OLA_WARN << "Not enough shownet data: offset=" << data_offset <<
", enc_len=" << enc_len << ", received_bytes=" << received_data_size;
return false;
}
if (!packet.slotSize[0]) {
OLA_WARN << "Malformed shownet packet, slotSize=" << packet.slotSize[0];
return false;
}
unsigned int start_channel = (packet.netSlot[0] - 1) % DMX_UNIVERSE_SIZE;
unsigned int universe_id = (packet.netSlot[0] - 1) / DMX_UNIVERSE_SIZE;
map<unsigned int, universe_handler>::iterator iter =
m_handlers.find(universe_id);
if (iter == m_handlers.end()) {
OLA_DEBUG << "Not interested in universe " << universe_id <<
", skipping ";
return false;
}
if (packet.slotSize[0] != enc_len) {
m_encoder.Decode(iter->second.buffer,
start_channel,
packet.data + data_offset,
enc_len);
} else {
iter->second.buffer->SetRange(start_channel,
packet.data + data_offset,
enc_len);
}
iter->second.closure->Run();
return true;
}
/*
* Populate a shownet data packet
*/
unsigned int ShowNetNode::PopulatePacket(shownet_data_packet *packet,
unsigned int universe,
const DmxBuffer &buffer) {
memset(packet, 0, sizeof(*packet));
// setup the fields in the shownet packet
packet->sigHi = SHOWNET_ID_HIGH;
packet->sigLo = SHOWNET_ID_LOW;
memcpy(packet->ip, &m_interface.ip_address, sizeof(packet->ip));
packet->netSlot[0] = (universe * DMX_UNIVERSE_SIZE) + 1;
packet->slotSize[0] = buffer.Size();
unsigned int enc_len = sizeof(packet->data);
if (!m_encoder.Encode(buffer, packet->data, enc_len))
OLA_WARN << "Failed to encode all data (used " << enc_len << " bytes";
packet->indexBlock[0] = MAGIC_INDEX_OFFSET;
packet->indexBlock[1] = MAGIC_INDEX_OFFSET + enc_len;
packet->packetCountHi = ShortGetHigh(m_packet_count);
packet->packetCountLo = ShortGetLow(m_packet_count);
strncpy(reinterpret_cast<char*>(packet->name), m_node_name.data(),
SHOWNET_NAME_LENGTH);
return sizeof(*packet) - sizeof(packet->data) + enc_len;
}
/*
* Setup the networking compoents.
*/
bool ShowNetNode::InitNetwork() {
m_socket = new UdpSocket();
if (!m_socket->Init()) {
OLA_WARN << "Socket init failed";
delete m_socket;
return false;
}
if (!m_socket->Bind(SHOWNET_PORT)) {
OLA_WARN << "Failed to bind to:" << SHOWNET_PORT;
delete m_socket;
return false;
}
if (!m_socket->EnableBroadcast()) {
OLA_WARN << "Failed to enable broadcasting";
delete m_socket;
return false;
}
m_socket->SetOnData(NewClosure(this, &ShowNetNode::SocketReady));
return true;
}
} // shownet
} // plugin
} // ola