Skip to content
Draft
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
40 changes: 40 additions & 0 deletions common/utils/UtilsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,24 @@
using ola::SequenceNumber;
using ola::utils::SplitUInt16;
using ola::utils::JoinUInt8;
using ola::utils::TruncateUInt16High;
using ola::utils::TruncateUInt16Low;

class UtilsTest: public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(UtilsTest);
CPPUNIT_TEST(testSequenceNumber);
CPPUNIT_TEST(testSplitUInt16);
CPPUNIT_TEST(testJoinUInt8);
CPPUNIT_TEST(testTruncateUInt16High);
CPPUNIT_TEST(testTruncateUInt16Low);
CPPUNIT_TEST_SUITE_END();

public:
void testSequenceNumber();
void testSplitUInt16();
void testJoinUInt8();
void testTruncateUInt16High();
void testTruncateUInt16Low();
};


Expand Down Expand Up @@ -127,3 +133,37 @@ void UtilsTest::testJoinUInt8() {
low = 0x01;
OLA_ASSERT_EQ(JoinUInt8(high, low), static_cast<uint16_t>(0x0001));
}

/*
* Test the TruncateUInt16High function
*/
void UtilsTest::testTruncateUInt16High() {
uint16_t input = 0xabcd;
OLA_ASSERT_EQ(TruncateUInt16High(input), static_cast<uint8_t>(0xab));

input = 0x0000;
OLA_ASSERT_EQ(TruncateUInt16High(input), static_cast<uint8_t>(0x00));

input = 0xffff;
OLA_ASSERT_EQ(TruncateUInt16High(input), static_cast<uint8_t>(0xff));

input = 0x0001;
OLA_ASSERT_EQ(TruncateUInt16High(input), static_cast<uint8_t>(0x00));
}

/*
* Test the TruncateUInt16Low function
*/
void UtilsTest::testTruncateUInt16Low() {
uint16_t input = 0xabcd;
OLA_ASSERT_EQ(TruncateUInt16Low(input), static_cast<uint8_t>(0xcd));

input = 0x0000;
OLA_ASSERT_EQ(TruncateUInt16Low(input), static_cast<uint8_t>(0x00));

input = 0xffff;
OLA_ASSERT_EQ(TruncateUInt16Low(input), static_cast<uint8_t>(0xff));

input = 0x0001;
OLA_ASSERT_EQ(TruncateUInt16Low(input), static_cast<uint8_t>(0x01));
}
21 changes: 21 additions & 0 deletions include/ola/util/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,27 @@ inline uint32_t JoinUInt8(uint8_t byte0, uint8_t byte1, uint8_t byte2,
(std::numeric_limits<uint8_t>::digits))
| byte3);
}


/**
* @brief Truncate a uint16_t keeping the high byte
* @param[in] input the uint16_t to truncate
* @return the high byte
*/
inline uint8_t TruncateUInt16High(uint16_t input) {
return static_cast<uint8_t>((input >> std::numeric_limits<uint8_t>::digits) &
std::numeric_limits<uint8_t>::max());
}


/**
* @brief Truncate a uint16_t keeping the low byte
* @param[in] input the uint16_t to truncate
* @return the low byte
*/
inline uint8_t TruncateUInt16Low(uint16_t input) {
return static_cast<uint8_t>(input & std::numeric_limits<uint8_t>::max());
}
} // namespace utils
} // namespace ola
#endif // INCLUDE_OLA_UTIL_UTILS_H_
42 changes: 39 additions & 3 deletions plugins/nanoleaf/NanoleafDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <memory>
#include <limits>
#include <set>
#include <string>
#include <vector>

Expand All @@ -43,9 +44,13 @@ namespace nanoleaf {
using ola::network::IPV4Address;
using ola::network::IPV4SocketAddress;
using std::auto_ptr;
using std::set;
using std::string;
using std::vector;

const char NanoleafDevice::VERSION_V1_TEXT[] = "v1";
const char NanoleafDevice::VERSION_V2_TEXT[] = "v2";

/*
* Create a new Nanoleaf Device
*/
Expand All @@ -68,7 +73,19 @@ NanoleafDevice::NanoleafDevice(
* @return true on success, false on failure
*/
bool NanoleafDevice::StartHook() {
vector<uint8_t> panels;
string text_version = m_preferences->GetValue(VersionKey());

NanoleafNode::NanoleafVersion version = NanoleafNode::VERSION_V1;
if (text_version == VERSION_V1_TEXT) {
version = NanoleafNode::VERSION_V1;
} else if (text_version == VERSION_V2_TEXT) {
version = NanoleafNode::VERSION_V2;
} else {
OLA_WARN << "Unknown Nanoleaf protocol version " << text_version
<< ", defaulting to v1";
}

vector<uint16_t> panels;
vector<string> panel_list;
StringSplit(m_preferences->GetValue(PanelsKey()), &panel_list, ",");
vector<string>::const_iterator iter = panel_list.begin();
Expand All @@ -77,7 +94,8 @@ bool NanoleafDevice::StartHook() {
continue;
}

uint8_t panel;
// TODO(Peter): Check < 255 if version 1
uint16_t panel;
if (!StringToInt(*iter, &panel)) {
OLA_WARN << "Invalid value for panel: " << *iter;
return false;
Expand All @@ -91,7 +109,11 @@ bool NanoleafDevice::StartHook() {
return false;
}

m_node = new NanoleafNode(m_plugin_adaptor, panels);
// TODO(Peter): Check and warn if we have more than a universe of panels,
// possibly truncate the extra panels too

// Don't bother passing in a source socket, let the node generate it's own
m_node = new NanoleafNode(m_plugin_adaptor, panels, NULL, version);

if (!m_node->Start()) {
delete m_node;
Expand Down Expand Up @@ -124,9 +146,23 @@ string NanoleafDevice::PanelsKey() const {
}


string NanoleafDevice::VersionKey() const {
return m_controller.ToString() + "-version";
}


void NanoleafDevice::SetDefaults() {
// Set device options
m_preferences->SetDefaultValue(PanelsKey(), StringValidator(), "");

set<string> valid_versions;
valid_versions.insert(VERSION_V1_TEXT);
valid_versions.insert(VERSION_V2_TEXT);

m_preferences->SetDefaultValue(VersionKey(),
SetValidator<string>(valid_versions),
VERSION_V1_TEXT);

m_preferences->SetDefaultValue(
IPPortKey(),
UIntValidator(1, std::numeric_limits<uint16_t>::max()),
Expand Down
36 changes: 20 additions & 16 deletions plugins/nanoleaf/NanoleafDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,33 @@ namespace nanoleaf {

class NanoleafDevice: public ola::Device {
public:
NanoleafDevice(AbstractPlugin *owner,
class Preferences *preferences,
class PluginAdaptor *plugin_adaptor,
const ola::network::IPV4Address &controller);
NanoleafDevice(AbstractPlugin *owner,
class Preferences *preferences,
class PluginAdaptor *plugin_adaptor,
const ola::network::IPV4Address &controller);

std::string DeviceId() const;
std::string DeviceId() const;

protected:
bool StartHook();
void PrePortStop();
void PostPortStop();
bool StartHook();
void PrePortStop();
void PostPortStop();

private:
class NanoleafNode *m_node;
class Preferences *m_preferences;
class PluginAdaptor *m_plugin_adaptor;
const ola::network::IPV4Address m_controller;
class NanoleafNode *m_node;
class Preferences *m_preferences;
class PluginAdaptor *m_plugin_adaptor;
const ola::network::IPV4Address m_controller;

void SetDefaults();
std::string IPPortKey() const;
std::string PanelsKey() const;
void SetDefaults();
std::string IPPortKey() const;
std::string PanelsKey() const;
std::string VersionKey() const;

static const uint16_t DEFAULT_STREAMING_PORT = 60221;
static const uint16_t DEFAULT_STREAMING_PORT = 60221;

static const char VERSION_V1_TEXT[];
static const char VERSION_V2_TEXT[];
};
} // namespace nanoleaf
} // namespace plugin
Expand Down
51 changes: 40 additions & 11 deletions plugins/nanoleaf/NanoleafNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "ola/Constants.h"
#include "ola/Logging.h"
#include "ola/network/SocketAddress.h"
#include "ola/util/Utils.h"
#include "plugins/nanoleaf/NanoleafNode.h"

namespace ola {
Expand All @@ -35,6 +36,7 @@ namespace nanoleaf {

using ola::network::IPV4SocketAddress;
using ola::network::UDPSocket;
using ola::utils::TruncateUInt16Low;
using std::auto_ptr;
using std::vector;

Expand All @@ -44,11 +46,13 @@ using std::vector;
* @param socket a UDPSocket or Null. Ownership is transferred.
*/
NanoleafNode::NanoleafNode(ola::io::SelectServerInterface *ss,
std::vector<uint8_t> panels,
ola::network::UDPSocketInterface *socket)
std::vector<uint16_t> panels,
ola::network::UDPSocketInterface *socket,
NanoleafVersion version)
: m_running(false),
m_ss(ss),
m_panels(panels),
m_version(version),
m_output_stream(&m_output_queue),
m_socket(socket) {
}
Expand Down Expand Up @@ -107,17 +111,42 @@ bool NanoleafNode::SendDMX(const IPV4SocketAddress &target,
<< ", got " << buffer.Size();
}

uint8_t panel_count = std::min(
static_cast<uint8_t>(m_panels.size()),
static_cast<uint8_t>(floor(buffer.Size() / NANOLEAF_SLOTS_PER_PANEL)));
// Although the field is now a uint16_t in some cases, we're still limited to
// 170 panels in one DMX universe unless we added the opportunity to address
// them so they overlapped
uint16_t panel_count = std::min(
static_cast<uint16_t>(m_panels.size()),
static_cast<uint16_t>(floor(buffer.Size() / NANOLEAF_SLOTS_PER_PANEL)));

m_output_queue.Clear();
m_output_stream << panel_count;
for (uint8_t i = 0; i < panel_count; i++) {
m_output_stream << m_panels[i] << NANOLEAF_FRAME_COUNT;
m_output_stream.Write(buffer.GetRaw() + (i * NANOLEAF_SLOTS_PER_PANEL),
NANOLEAF_SLOTS_PER_PANEL);
m_output_stream << NANOLEAF_WHITE_LEVEL << NANOLEAF_TRANSITION_TIME;
if (m_version == VERSION_V1) {
// This needs truncating as v1 only supports up to 255 panels but we use a
// uint16_t so we can use the same setup for v2
m_output_stream << TruncateUInt16Low(panel_count);
for (uint16_t i = 0; i < panel_count; i++) {
// This needs truncating as v1 only supports panel IDs up to 255 but we
// use a uint16_t so we can use the same setup for v2
m_output_stream << TruncateUInt16Low(m_panels[i])
<< NANOLEAF_FRAME_COUNT_V1;
m_output_stream.Write(buffer.GetRaw() + (i * NANOLEAF_SLOTS_PER_PANEL),
NANOLEAF_SLOTS_PER_PANEL);
m_output_stream << NANOLEAF_WHITE_LEVEL << NANOLEAF_TRANSITION_TIME_V1;
}
} else if (m_version == VERSION_V2) {
// This is just 16 bit in v2
m_output_stream << panel_count;
for (uint16_t i = 0; i < panel_count; i++) {
// This is just 16 bit in v2
m_output_stream << m_panels[i];
// No frame count in v2
// TODO(Peter): Check this doesn't flip the bytes)
m_output_stream.Write(buffer.GetRaw() + (i * NANOLEAF_SLOTS_PER_PANEL),
NANOLEAF_SLOTS_PER_PANEL);
// Transition time is a uint16_t in v2
m_output_stream << NANOLEAF_WHITE_LEVEL << NANOLEAF_TRANSITION_TIME_V2;
}
} else {
OLA_WARN << "Unknown Nanoleaf protocol version " << m_version;
}

// m_output_queue.Dump(&std::cerr);
Expand Down
60 changes: 36 additions & 24 deletions plugins/nanoleaf/NanoleafNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#include "ola/DmxBuffer.h"
#include "ola/base/Macro.h"
#include "ola/io/OutputStream.h"
#include "ola/io/BigEndianStream.h"
#include "ola/io/IOQueue.h"
#include "ola/io/SelectServerInterface.h"
#include "ola/network/Interface.h"
Expand All @@ -39,36 +39,48 @@ namespace nanoleaf {

class NanoleafNode {
public:
NanoleafNode(ola::io::SelectServerInterface *ss,
std::vector<uint8_t> panels,
ola::network::UDPSocketInterface *socket = NULL);
virtual ~NanoleafNode();
// The different versions we support.
enum NanoleafVersion {
VERSION_V1,
VERSION_V2,
};

bool Start();
bool Stop();
NanoleafNode(ola::io::SelectServerInterface *ss,
std::vector<uint16_t> panels,
ola::network::UDPSocketInterface *socket = NULL,
NanoleafVersion version = VERSION_V1);
virtual ~NanoleafNode();

// The following apply to Input Ports (those which send data)
bool SendDMX(const ola::network::IPV4SocketAddress &target,
const ola::DmxBuffer &buffer);
bool Start();
bool Stop();

// The following apply to Input Ports (those which send data)
bool SendDMX(const ola::network::IPV4SocketAddress &target,
const ola::DmxBuffer &buffer);

private:
bool m_running;
ola::io::SelectServerInterface *m_ss;
std::vector<uint8_t> m_panels;
ola::io::IOQueue m_output_queue;
ola::io::OutputStream m_output_stream;
ola::network::Interface m_interface;
std::auto_ptr<ola::network::UDPSocketInterface> m_socket;
bool m_running;
ola::io::SelectServerInterface *m_ss;
std::vector<uint16_t> m_panels;
NanoleafVersion m_version;
ola::io::IOQueue m_output_queue;
// v2 protocol is BigEndian
ola::io::BigEndianOutputStream m_output_stream;
ola::network::Interface m_interface;
std::auto_ptr<ola::network::UDPSocketInterface> m_socket;

void SocketReady();
bool InitNetwork();

static const uint8_t NANOLEAF_FRAME_COUNT_V1 = 0x01;
static const uint8_t NANOLEAF_TRANSITION_TIME_V1 = 0x01;

void SocketReady();
bool InitNetwork();
static const uint16_t NANOLEAF_TRANSITION_TIME_V2 = 0x0001;

static const uint8_t NANOLEAF_FRAME_COUNT = 0x01;
static const uint8_t NANOLEAF_WHITE_LEVEL = 0x00;
static const uint8_t NANOLEAF_TRANSITION_TIME = 0x01;
static const uint8_t NANOLEAF_SLOTS_PER_PANEL = 3;
static const uint8_t NANOLEAF_WHITE_LEVEL = 0x00;
static const uint8_t NANOLEAF_SLOTS_PER_PANEL = 3;

DISALLOW_COPY_AND_ASSIGN(NanoleafNode);
DISALLOW_COPY_AND_ASSIGN(NanoleafNode);
};
} // namespace nanoleaf
} // namespace plugin
Expand Down
Loading
Loading