Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Anyma, Eurolite and Fadecandy widgets. #543

Merged
merged 3 commits into from
Nov 27, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions plugins/usbdmx/AnymauDMX.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* AnymauDMX.cpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Firstly, personally I'm still in favour of the files just being manufacturer, rather than manufacturer-device.

Regardless of that, this file doesn't match the Camel Case style we're using elsewhere, and just looks a bit odd to me. I wonder if it should be AnymaUDmx.cpp?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual name of the device is uDMX.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, but we don't stick to that with include/ola/DmxBuffer.h; although we're not very consistent looking at include/ola/http/HTTPServer.h.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I standardized on 'DMX' for the client code but DmxBuffer predates that. I'd prefer to use DMX going forward.

* The synchronous and asynchronous Anyma uDMX widgets.
* Copyright (C) 2014 Simon Newton
*/

#include "plugins/usbdmx/AnymauDMX.h"

#include <unistd.h>
#include <string>

#include "ola/Logging.h"
#include "ola/Constants.h"
#include "plugins/usbdmx/AsyncUsbSender.h"
#include "plugins/usbdmx/LibUsbAdaptor.h"
#include "plugins/usbdmx/ThreadedUsbSender.h"

namespace ola {
namespace plugin {
namespace usbdmx {

using std::string;

namespace {

static const unsigned int URB_TIMEOUT_MS = 500;
static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002;

} // namespace

// AnymaThreadedSender
// -----------------------------------------------------------------------------

/*
* Sends messages to a Anyma device in a separate thread.
*/
class AnymaThreadedSender: public ThreadedUsbSender {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also feels inconsistent when the classes don't match the style of the filename (manufacturer only). I'd still be in favour of dropping the device from the filename.

public:
AnymaThreadedSender(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
libusb_device_handle *handle)
: ThreadedUsbSender(usb_device, handle),
m_adaptor(adaptor) {
}

private:
LibUsbAdaptor* const m_adaptor;

bool TransmitBuffer(libusb_device_handle *handle,
const DmxBuffer &buffer);
};

bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle,
const DmxBuffer &buffer) {
int r = m_adaptor->ControlTransfer(
handle,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, // bmRequestType
UDMX_SET_CHANNEL_RANGE, // bRequest
buffer.Size(), // wValue
0, // wIndex
const_cast<unsigned char*>(buffer.GetRaw()), // data
buffer.Size(), // wLength
URB_TIMEOUT_MS); // timeout
// Sometimes we get PIPE errors here, those are non-fatal
return r > 0 || r == LIBUSB_ERROR_PIPE;
}


// SynchronousAnymauDMX
// -----------------------------------------------------------------------------

SynchronousAnymauDMX::SynchronousAnymauDMX(LibUsbAdaptor *adaptor,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make up your mind. :)

libusb_device *usb_device,
const string &serial)
: AnymauDMX(adaptor, serial),
m_usb_device(usb_device) {
}

bool SynchronousAnymauDMX::Init() {
libusb_device_handle *usb_handle;

bool ok = m_adaptor->OpenDeviceAndClaimInterface(
m_usb_device, 0, &usb_handle);
if (!ok) {
return false;
}

std::auto_ptr<AnymaThreadedSender> sender(
new AnymaThreadedSender(m_adaptor, m_usb_device, usb_handle));
if (!sender->Start()) {
return false;
}
m_sender.reset(sender.release());
return true;
}

bool SynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer) {
return m_sender.get() ? m_sender->SendDMX(buffer) : false;
}

// AnymaAsyncUsbSender
// -----------------------------------------------------------------------------
class AnymaAsyncUsbSender : public AsyncUsbSender {
public:
AnymaAsyncUsbSender(LibUsbAdaptor *adaptor, libusb_device *usb_device)
: AsyncUsbSender(adaptor, usb_device) {
m_control_setup_buffer =
new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE];
}

~AnymaAsyncUsbSender() {
CancelTransfer();
delete[] m_control_setup_buffer;
}

libusb_device_handle* SetupHandle() {
libusb_device_handle *usb_handle;
bool ok = m_adaptor->OpenDeviceAndClaimInterface(
m_usb_device, 0, &usb_handle);
return ok ? usb_handle : NULL;
}

bool PerformTransfer(const DmxBuffer &buffer) {
m_adaptor->FillControlSetup(
m_control_setup_buffer,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, // bmRequestType
UDMX_SET_CHANNEL_RANGE, // bRequest
buffer.Size(), // wValue
0, // wIndex
buffer.Size()); // wLength

unsigned int length = DMX_UNIVERSE_SIZE;
buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length);

FillControlTransfer(m_control_setup_buffer, URB_TIMEOUT_MS);
return SubmitTransfer() == 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brackets.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need parens here though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you don't, it just feels it would make it a bit more readable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to make you happy :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

}

private:
uint8_t *m_control_setup_buffer;

DISALLOW_COPY_AND_ASSIGN(AnymaAsyncUsbSender);
};

// AsynchronousAnymauDMX
// -----------------------------------------------------------------------------

AsynchronousAnymauDMX::AsynchronousAnymauDMX(
LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const string &serial)
: AnymauDMX(adaptor, serial) {
m_sender.reset(new AnymaAsyncUsbSender(m_adaptor, usb_device));
}

bool AsynchronousAnymauDMX::Init() {
return m_sender->Init();
}

bool AsynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer) {
return m_sender->SendDMX(buffer);
}
} // namespace usbdmx
} // namespace plugin
} // namespace ola
121 changes: 121 additions & 0 deletions plugins/usbdmx/AnymauDMX.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* AnymauDMX.h
* The synchronous and asynchronous Anyma uDMX widgets.
* Copyright (C) 2014 Simon Newton
*/

#ifndef PLUGINS_USBDMX_ANYMAUDMX_H_
#define PLUGINS_USBDMX_ANYMAUDMX_H_

#include <libusb.h>
#include <memory>
#include <string>

#include "ola/DmxBuffer.h"
#include "ola/base/Macro.h"
#include "ola/thread/Mutex.h"
#include "plugins/usbdmx/Widget.h"

namespace ola {
namespace plugin {
namespace usbdmx {

/**
* @brief The base class for Anyma Widgets.
*/
class AnymauDMX: public BaseWidget {
public:
/**
* @brief Create a new AnymauDMX.
* @param adaptor the LibUsbAdaptor to use.
* @param serial the serial number of the widget.
*/
AnymauDMX(LibUsbAdaptor *adaptor,
const std::string &serial)
: BaseWidget(adaptor),
m_serial(serial) {}

virtual ~AnymauDMX() {}

/**
* @brief Get the serial number of this widget.
* @returns The serial number of the widget.
*/
std::string SerialNumber() const {
return m_serial;
}

private:
std::string m_serial;
};

/**
* @brief An Anyma widget that uses synchronous libusb operations.
*
* Internally this spawns a new thread to avoid blocking SendDMX() calls.
*/
class SynchronousAnymauDMX: public AnymauDMX {
public:
/**
* @brief Create a new SynchronousAnymauDMX.
* @param adaptor the LibUsbAdaptor to use.
* @param usb_device the libusb_device to use for the widget.
* @param serial the serial number of the widget.
*/
SynchronousAnymauDMX(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const std::string &serial);

bool Init();

bool SendDMX(const DmxBuffer &buffer);

private:
libusb_device* const m_usb_device;
std::auto_ptr<class AnymaThreadedSender> m_sender;

DISALLOW_COPY_AND_ASSIGN(SynchronousAnymauDMX);
};

/**
* @brief An Anyma widget that uses asynchronous libusb operations.
*/
class AsynchronousAnymauDMX : public AnymauDMX {
public:
/**
* @brief Create a new AsynchronousAnymauDMX.
* @param adaptor the LibUsbAdaptor to use.
* @param usb_device the libusb_device to use for the widget.
* @param serial the serial number of the widget.
*/
AsynchronousAnymauDMX(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const std::string &serial);

bool Init();

bool SendDMX(const DmxBuffer &buffer);

private:
std::auto_ptr<class AnymaAsyncUsbSender> m_sender;

DISALLOW_COPY_AND_ASSIGN(AsynchronousAnymauDMX);
};
} // namespace usbdmx
} // namespace plugin
} // namespace ola
#endif // PLUGINS_USBDMX_ANYMAUDMX_H_
88 changes: 88 additions & 0 deletions plugins/usbdmx/AnymauDMXFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* AnymauDMXFactory.cpp
* The factory for Anyma uDMX widgets.
* Copyright (C) 2014 Simon Newton
*/

#include "plugins/usbdmx/AnymauDMXFactory.h"

#include "ola/Logging.h"
#include "ola/base/Flags.h"
#include "plugins/usbdmx/AnymauDMX.h"
#include "plugins/usbdmx/LibUsbAdaptor.h"

DECLARE_bool(use_async_libusb);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

namespace ola {
namespace plugin {
namespace usbdmx {

const char AnymauDMXFactory::EXPECTED_MANUFACTURER[] = "www.anyma.ch";
const char AnymauDMXFactory::EXPECTED_PRODUCT[] = "uDMX";
const uint16_t AnymauDMXFactory::PRODUCT_ID = 0x05DC;
const uint16_t AnymauDMXFactory::VENDOR_ID = 0x16C0;

bool AnymauDMXFactory::DeviceAdded(
WidgetObserver *observer,
libusb_device *usb_device,
const struct libusb_device_descriptor &descriptor) {
if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID ||
HasDevice(usb_device)) {
return false;
}

OLA_INFO << "Found a new Anyma device";
LibUsbAdaptor::DeviceInformation info;
if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) {
return false;
}

if (!m_adaptor->CheckManufacturer(EXPECTED_MANUFACTURER, info)) {
return false;
}

if (!m_adaptor->CheckProduct(EXPECTED_PRODUCT, info)) {
return false;
}

// Some Anyma devices don't have serial numbers. Since there isn't another
// good way to uniquely identify a USB device, we only support one of these
// types of devices per host.
if (info.serial.empty()) {
if (m_missing_serial_number) {
OLA_WARN << "Failed to read serial number or serial number empty. "
<< "We can only support one device without a serial number.";
return false;
} else {
OLA_WARN << "Failed to read serial number from " << info.manufacturer
<< " : " << info.product
<< " the device probably doesn't have one";
m_missing_serial_number = true;
}
}

AnymauDMX *widget = NULL;
if (FLAGS_use_async_libusb) {
widget = new AsynchronousAnymauDMX(m_adaptor, usb_device, info.serial);
} else {
widget = new SynchronousAnymauDMX(m_adaptor, usb_device, info.serial);
}
return AddWidget(observer, usb_device, widget);
}
} // namespace usbdmx
} // namespace plugin
} // namespace ola
Loading