/* * Check that SetChannel works */ void DmxBufferTest::testSetChannel() { DmxBuffer buffer; buffer.SetChannel(1, 10); buffer.SetChannel(10, 50); uint8_t expected[DMX_UNIVERSE_SIZE]; memset(expected, 0, DMX_UNIVERSE_SIZE); expected[1] = 10; expected[10] = 50; OLA_ASSERT_EQ((unsigned int) DMX_UNIVERSE_SIZE, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(expected, buffer.GetRaw(), buffer.Size())); // Check we can't set values greater than the buffer size buffer.SetChannel(999, 50); OLA_ASSERT_EQ((unsigned int) DMX_UNIVERSE_SIZE, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(expected, buffer.GetRaw(), buffer.Size())); // Check we can't set values outside the current valida data range unsigned int slice_size = 20; buffer.Set(expected, slice_size); buffer.SetChannel(30, 90); buffer.SetChannel(200, 10); OLA_ASSERT_EQ(slice_size, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(expected, buffer.GetRaw(), buffer.Size())); }
/* * Test receiving works. */ void RobeWidgetTest::testReceive() { DmxBuffer buffer; buffer.SetFromString("0,1,2,3,4"); // change to recv mode & setup the callback m_endpoint->AddExpectedRobeMessage( DMX_IN_REQUEST_LABEL, NULL, 0, ola::NewSingleCallback(this, &RobeWidgetTest::Terminate)); m_widget->ChangeToReceiveMode(); m_ss.Run(); m_endpoint->Verify(); m_widget->SetDmxCallback( ola::NewCallback(this, &RobeWidgetTest::NewDMXData)); CPPUNIT_ASSERT(!m_new_dmx_data); // now send some data m_endpoint->SendUnsolicitedRobeData(DMX_IN_RESPONSE_LABEL, buffer.GetRaw(), buffer.Size()); m_ss.Run(); CPPUNIT_ASSERT(m_new_dmx_data); const DmxBuffer &new_data = m_widget->FetchDMX(); CPPUNIT_ASSERT(buffer == new_data); }
/* * Send some DMX data */ bool KiNetNode::SendDMX(const IPV4Address &target_ip, const DmxBuffer &buffer) { static const uint8_t port = 0; static const uint8_t flags = 0; static const uint16_t timer_val = 0; static const uint32_t universe = 0xffffffff; if (!buffer.Size()) { OLA_DEBUG << "Not sending 0 length packet"; return true; } m_output_queue.Clear(); PopulatePacketHeader(KINET_DMX_MSG); m_output_stream << port << flags << timer_val << universe; m_output_stream << DMX512_START_CODE; m_output_stream.Write(buffer.GetRaw(), buffer.Size()); IPV4SocketAddress target(target_ip, KINET_PORT); bool ok = m_socket->SendTo(&m_output_queue, target); if (!ok) OLA_WARN << "Failed to send KiNet DMX packet"; if (!m_output_queue.Empty()) { OLA_WARN << "Failed to send complete KiNet packet"; m_output_queue.Clear(); } return ok; }
/* * Send a dmx msg. * This has the nasty property of blocking if we remove the device * TODO: fix this */ bool StageProfiWidget::SendDmx(const DmxBuffer &buffer) const { uint16_t index = 0; while (index < buffer.Size()) { unsigned int size = std::min((unsigned int) DMX_MSG_LEN, buffer.Size() - index); Send255(index, buffer.GetRaw() + index, size); index += size; } return true; }
bool JaRulePortHandleImpl::SendDMX(const DmxBuffer &buffer) { if (m_dmx_in_progress) { m_dmx = buffer; m_dmx_queued = true; } else { m_dmx_in_progress = true; m_port->SendCommand(JARULE_CMD_TX_DMX, buffer.GetRaw(), buffer.Size(), m_dmx_callback); } return true; }
/* * Check that SetRangeToValue works */ void DmxBufferTest::testSetRangeToValue() { const uint8_t RANGE_DATA[] = {50, 50, 50, 50, 50}; DmxBuffer buffer; OLA_ASSERT_FALSE(buffer.SetRangeToValue(600, 50, 2)); unsigned int range_size = 5; OLA_ASSERT_TRUE(buffer.SetRangeToValue(0, 50, range_size)); OLA_ASSERT_EQ((unsigned int) DMX_UNIVERSE_SIZE, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(RANGE_DATA, buffer.GetRaw(), range_size)); // setting outside the value range should fail buffer.Reset(); OLA_ASSERT_FALSE(buffer.SetRange(10, TEST_DATA, range_size)); }
/* * Call Encode then Decode and check the results */ void RunLengthEncoderTest::checkEncodeDecode(const uint8_t *data, unsigned int data_size) { DmxBuffer src(data, data_size); DmxBuffer dst; unsigned int dst_size = ola::DMX_UNIVERSE_SIZE; memset(m_dst, 0, dst_size); OLA_ASSERT_TRUE(m_encoder.Encode(src, m_dst, &dst_size)); OLA_ASSERT_TRUE(m_encoder.Decode(0, m_dst, dst_size, &dst)); OLA_ASSERT_TRUE(src == dst); OLA_ASSERT_EQ(dst.Size(), data_size); OLA_ASSERT_NE(0, memcmp(data, dst.GetRaw(), dst.Size())); }
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; }
/* * Write operation * @param buffer the DmxBuffer to write * @return true on success, false on failure */ bool Dmx4LinuxOutputPort::WriteDMX(const DmxBuffer &buffer, uint8_t priority) { int offset = DMX_UNIVERSE_SIZE * m_d4l_universe; if (lseek(m_socket->WriteDescriptor(), offset, SEEK_SET) == offset) { ssize_t r = m_socket->Send(buffer.GetRaw(), buffer.Size()); if ((uint) r != buffer.Size()) { OLA_WARN << "only wrote " << r << "/" << buffer.Size() << " bytes: " << strerror(errno); return false; } } else { OLA_WARN << "failed to seek: " << strerror(errno); return false; } return true; }
/** * Send a DMXBuffer as a blob to a set of targets */ bool OSCNode::SendBlob(const DmxBuffer &dmx_data, const OSCTargetVector &targets) { // create the new OSC blob lo_blob osc_data = lo_blob_new(dmx_data.Size(), dmx_data.GetRaw()); bool ok = true; // iterate over all the targets, and send to each one. OSCTargetVector::const_iterator target_iter = targets.begin(); for (; target_iter != targets.end(); ++target_iter) { OLA_DEBUG << "Sending to " << (*target_iter)->socket_address; int ret = lo_send_from((*target_iter)->liblo_address, m_osc_server, LO_TT_IMMEDIATE, (*target_iter)->osc_address.c_str(), "b", osc_data); ok &= (ret > 0); } // free the blob lo_blob_free(osc_data); return ok; }
/* * Check that SetRange works. */ void DmxBufferTest::testSetRange() { unsigned int data_size = sizeof(TEST_DATA); DmxBuffer buffer; OLA_ASSERT_FALSE(buffer.SetRange(0, NULL, data_size)); OLA_ASSERT_FALSE(buffer.SetRange(600, TEST_DATA, data_size)); // Setting an uninitialized buffer calls blackout first OLA_ASSERT_TRUE(buffer.SetRange(0, TEST_DATA, data_size)); OLA_ASSERT_EQ((unsigned int) DMX_UNIVERSE_SIZE, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw(), data_size)); // try overrunning the buffer OLA_ASSERT_TRUE(buffer.SetRange(DMX_UNIVERSE_SIZE - 2, TEST_DATA, data_size)); OLA_ASSERT_EQ((unsigned int) DMX_UNIVERSE_SIZE, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw() + DMX_UNIVERSE_SIZE - 2, 2)); // reset the buffer so that the valid data is 0, and try again buffer.Reset(); OLA_ASSERT_TRUE(buffer.SetRange(0, TEST_DATA, data_size)); OLA_ASSERT_EQ((unsigned int) data_size, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw(), data_size)); // setting past the end of the valid data should fail OLA_ASSERT_FALSE(buffer.SetRange(50, TEST_DATA, data_size)); OLA_ASSERT_EQ((unsigned int) data_size, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw(), buffer.Size())); // overwrite part of the valid data unsigned int offset = 2; OLA_ASSERT_TRUE(buffer.SetRange(offset, TEST_DATA, data_size)); OLA_ASSERT_EQ((unsigned int) data_size + offset, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw(), offset)); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw() + offset, buffer.Size() - offset)); // now try writing 1 channel past the valid data buffer.Reset(); OLA_ASSERT_TRUE(buffer.SetRange(0, TEST_DATA, data_size)); OLA_ASSERT_TRUE(buffer.SetRange(data_size, TEST_DATA, data_size)); OLA_ASSERT_EQ((unsigned int) data_size * 2, buffer.Size()); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw(), data_size)); OLA_ASSERT_EQ(0, memcmp(TEST_DATA, buffer.GetRaw() + data_size, data_size)); }
/* * Send the dmx out the widget * @return true on success, false on failure */ bool VellemanOutputPort::SendDMX(const DmxBuffer &buffer) { unsigned char usb_data[m_chunk_size]; unsigned int size = buffer.Size(); const uint8_t *data = buffer.GetRaw(); unsigned int i = 0; unsigned int n; // this could be up to 254 for the standard interface but then the shutdown // process gets wacky. Limit it to 100 for the standard and 255 for the // extended. unsigned int max_compressed_channels = m_chunk_size == UPGRADED_CHUNK_SIZE ? 254 : 100; unsigned int compressed_channel_count = m_chunk_size - 2; unsigned int channel_count = m_chunk_size - 1; memset(usb_data, 0, sizeof(usb_data)); if (m_chunk_size == UPGRADED_CHUNK_SIZE && size <= m_chunk_size - 2) { // if the upgrade is present and we can fit the data in a single packet // use the 7 message type usb_data[0] = 7; usb_data[1] = size; // number of channels in packet memcpy(usb_data + 2, data, std::min(size, m_chunk_size - 2)); } else { // otherwise use 4 to signal the start of frame for (n = 0; n < max_compressed_channels && n < size - compressed_channel_count && !data[n]; n++) { } usb_data[0] = 4; usb_data[1] = n + 1; // include start code memcpy(usb_data + 2, data + n, compressed_channel_count); i += n + compressed_channel_count; } if (!SendDataChunk(usb_data)) return false; while (i < size - channel_count) { for (n = 0; n < max_compressed_channels && n + i < size - compressed_channel_count && !data[i + n]; n++) { } if (n) { // we have leading zeros usb_data[0] = 5; usb_data[1] = n; memcpy(usb_data + 2, data + i + n, compressed_channel_count); i += n + compressed_channel_count; } else { usb_data[0] = 2; memcpy(usb_data + 1, data + i, channel_count); i += channel_count; } if (!SendDataChunk(usb_data)) return false; } // send the last channels if (m_chunk_size == UPGRADED_CHUNK_SIZE) { // if running in extended mode we can use the 6 message type to send // everything at once. usb_data[0] = 6; usb_data[1] = size - i; memcpy(usb_data + 2, data + i, size - i); if (!SendDataChunk(usb_data)) return false; } else { // else we use the 3 message type to send one at a time for (;i != size; i++) { usb_data[0] = 3; usb_data[1] = data[i]; if (!SendDataChunk(usb_data)) return false; } } return true; }
/* * Take a DMXBuffer and RunLengthEncode the data * @param src the DmxBuffer with the DMX data * @param data where to store the RLE data * @param size the size of the data segment, set to the amount of data encoded * @return true if we encoded all data, false if we ran out of space */ bool RunLengthEncoder::Encode(const DmxBuffer &src, uint8_t *data, unsigned int &data_size) { unsigned int src_size = src.Size(); unsigned int dst_size = data_size; unsigned int &dst_index = data_size; dst_index = 0; unsigned int i; for (i = 0; i < src_size && dst_index < dst_size;) { // j points to the first non-repeating value unsigned int j = i + 1; while (j < src_size && src.Get(i) == src.Get(j) && j - i < 0x7f) { j++; } // if the number of repeats is more than 2 // don't encode only two repeats, if (j - i > 2) { // if room left in dst buffer if (dst_size - dst_index > 1) { data[dst_index++] = (REPEAT_FLAG | (j - i)); data[dst_index++] = src.Get(i); } else { // else return what we have done so far return false; } i = j; } else { // this value doesn't repeat more than twice // find out where the next repeat starts // postcondition: j is one more than the last value we want to send for (j = i + 1; j < src_size - 2 && j - i < 0x7f; j++) { // at the end of the array if (j == src_size - 2) { j = src_size; break; } // if we're found a repeat of 3 or more stop here if (src.Get(j) == src.Get(j+1) && src.Get(j) == src.Get(j+2)) break; } if (j >= src_size - 2) j = src_size; // if we have enough room left for all the values if (dst_index + j - i < dst_size) { data[dst_index++] = j - i; memcpy(&data[dst_index], src.GetRaw() + i, j-i); dst_index += j - i; i = j; // see how much data we can get in } else if (dst_size - dst_index > 1) { unsigned int l = dst_size - dst_index -1; data[dst_index++] = l; memcpy(&data[dst_index], src.GetRaw() + i, l); dst_index += l; return false; } else { return false; } } } if (i < src_size) return false; else return true; }