/** * @brief Handle the set DMX command * @param request the HTTPRequest * @param response the HTTPResponse * @returns MHD_NO or MHD_YES */ int OladHTTPServer::HandleSetDmx(const HTTPRequest *request, HTTPResponse *response) { if (request->CheckParameterExists(HELP_PARAMETER)) { return ServeUsage(response, "POST u=[universe], d=[DMX data (a comma separated list of values)]"); } string dmx_data_str = request->GetPostParameter("d"); string uni_id = request->GetPostParameter("u"); unsigned int universe_id; if (!StringToInt(uni_id, &universe_id)) { return ServeHelpRedirect(response); } DmxBuffer buffer; buffer.SetFromString(dmx_data_str); if (!buffer.Size()) { return m_server.ServeError(response, "Invalid DMX string"); } ola::client::SendDMXArgs args( NewSingleCallback(this, &OladHTTPServer::HandleBoolResponse, response)); m_client.SendDMX(universe_id, buffer, args); return MHD_YES; }
/* * 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; }
/* * Send a DMX msg. */ bool RenardWidget::SendDmx(const DmxBuffer &buffer) { unsigned int channels = std::max((unsigned int)0, std::min((unsigned int) m_channels + m_dmxOffset, buffer.Size()) - m_dmxOffset); OLA_DEBUG << "Sending " << static_cast<int>(channels) << " channels"; // Max buffer size for worst case scenario (escaping + padding) unsigned int bufferSize = channels * 2 + 10; uint8_t msg[bufferSize]; int dataToSend = 0; for (unsigned int i = 0; i < channels; i++) { if ((i % RENARD_CHANNELS_IN_BANK) == 0) { if (m_byteCounter >= RENARD_BYTES_BETWEEN_PADDING) { // Send PAD every 100 (or so) bytes. Note that the counter is per // device, so the counter should span multiple calls to SendDMX. msg[dataToSend++] = RENARD_COMMAND_PAD; m_byteCounter = 0; } // Send address msg[dataToSend++] = RENARD_COMMAND_START_PACKET; msg[dataToSend++] = m_startAddress + (i / RENARD_CHANNELS_IN_BANK); m_byteCounter += 2; } uint8_t b = buffer.Get(m_dmxOffset + i); // Escaping magic bytes switch (b) { case RENARD_COMMAND_PAD: msg[dataToSend++] = RENARD_COMMAND_ESCAPE; msg[dataToSend++] = RENARD_ESCAPE_PAD; m_byteCounter += 2; break; case RENARD_COMMAND_START_PACKET: msg[dataToSend++] = RENARD_COMMAND_ESCAPE; msg[dataToSend++] = RENARD_ESCAPE_START_PACKET; m_byteCounter += 2; break; case RENARD_COMMAND_ESCAPE: msg[dataToSend++] = RENARD_COMMAND_ESCAPE; msg[dataToSend++] = RENARD_ESCAPE_ESCAPE; m_byteCounter += 2; break; default: msg[dataToSend++] = b; m_byteCounter++; break; } OLA_DEBUG << "Setting Renard " << m_startAddress + (i / RENARD_CHANNELS_IN_BANK) << "/" << ((i % RENARD_CHANNELS_IN_BANK) + 1) << " to " << static_cast<int>(b); } int bytes_sent = m_socket->Send(msg, dataToSend); OLA_DEBUG << "Sending DMX, sent " << bytes_sent << " bytes"; return true; }
/* * Update the screen with new values */ void DmxMonitor::Values() { int i = 0, x, y, z = first_channel; /* values */ for (y = ROWS_PER_CHANNEL_ROW; y < LINES && z < DMX_UNIVERSE_SIZE && i < static_cast<int>(channels_per_screen); y += ROWS_PER_CHANNEL_ROW) { move(y, 0); for (x = 0; x < static_cast<int>(channels_per_line) && z < DMX_UNIVERSE_SIZE && i < static_cast<int>(channels_per_screen); x++, z++, i++) { const int d = m_buffer.Get(z); switch (d) { case DMX_MIN_CHANNEL_VALUE: (void) attrset(palette[ZERO]); break; case DMX_MAX_CHANNEL_VALUE: (void) attrset(palette[FULL]); break; default: (void) attrset(palette[NORM]); } if (static_cast<int>(z) == current_channel) attron(A_REVERSE); switch (display_mode) { case DISP_MODE_HEX: if (d == 0) { if (static_cast<int>(m_buffer.Size()) <= z) { addstr("--- "); } else { addstr(" "); } } else { printw(" %02x ", d); } break; case DISP_MODE_DEC: if (d == 0) { if (static_cast<int>(m_buffer.Size()) <= z) { addstr("--- "); } else { addstr(" "); } } else if (d < 100) { printw(" %02d ", d); } else { printw("%03d ", d); } break; case DISP_MODE_DMX: default: switch (d) { case DMX_MIN_CHANNEL_VALUE: if (static_cast<int>(m_buffer.Size()) <= z) { addstr("--- "); } else { addstr(" "); } break; case DMX_MAX_CHANNEL_VALUE: addstr(" FL "); break; default: printw(" %02d ", (d * 100) / DMX_MAX_CHANNEL_VALUE); } } } } }
/** * Check that we receive OSC messages correctly. */ void OSCNodeTest::testReceive() { DmxBuffer expected_data; // Register the test OSC Address with the OSCNode using the DMXHandler as the // callback. OLA_ASSERT_TRUE(m_osc_node->RegisterAddress( TEST_OSC_ADDRESS, NewCallback(this, &OSCNodeTest::DMXHandler))); // Attempt to register the same address with a different path, this should // return false OLA_ASSERT_FALSE(m_osc_node->RegisterAddress( TEST_OSC_ADDRESS, NewCallback(this, &OSCNodeTest::DMXHandler))); // Using our test UDP socket, send the OSC_BLOB_DATA to the default OSC // port. The OSCNode should receive the packet and call DMXHandler. IPV4SocketAddress dest_address(IPV4Address::Loopback(), m_osc_node->ListeningPort()); // send a single float update m_udp_socket.SendTo(OSC_SINGLE_FLOAT_DATA, sizeof(OSC_SINGLE_FLOAT_DATA), dest_address); m_ss.Run(); OLA_ASSERT_EQ(512u, m_received_data.Size()); expected_data.SetChannel(0, 127); OLA_ASSERT_EQ(expected_data, m_received_data); // now send a blob update m_udp_socket.SendTo(OSC_BLOB_DATA, sizeof(OSC_BLOB_DATA), dest_address); // Run the SelectServer, this will return either when DMXHandler // completes, or the abort timeout triggers. m_ss.Run(); OLA_ASSERT_EQ(11u, m_received_data.Size()); expected_data.SetFromString("0,1,2,3,4,5,6,7,8,9,10"); OLA_ASSERT_EQ(expected_data, m_received_data); // Now try sending a float update. m_udp_socket.SendTo(OSC_SINGLE_FLOAT_DATA, sizeof(OSC_SINGLE_FLOAT_DATA), dest_address); m_ss.Run(); OLA_ASSERT_EQ(11u, m_received_data.Size()); expected_data.SetChannel(0, 127); OLA_ASSERT_EQ(expected_data, m_received_data); // Now try sending an int update. m_udp_socket.SendTo(OSC_SINGLE_INT_DATA, sizeof(OSC_SINGLE_INT_DATA), dest_address); m_ss.Run(); OLA_ASSERT_EQ(11u, m_received_data.Size()); expected_data.SetChannel(5, 140); OLA_ASSERT_EQ(expected_data, m_received_data); // An 'ii' update m_udp_socket.SendTo(OSC_INT_TUPLE_DATA, sizeof(OSC_INT_TUPLE_DATA), dest_address); m_ss.Run(); OLA_ASSERT_EQ(11u, m_received_data.Size()); expected_data.SetChannel(7, 90); OLA_ASSERT_EQ(expected_data, m_received_data); // An 'if' update m_udp_socket.SendTo(OSC_FLOAT_TUPLE_DATA, sizeof(OSC_FLOAT_TUPLE_DATA), dest_address); m_ss.Run(); OLA_ASSERT_EQ(11u, m_received_data.Size()); expected_data.SetChannel(8, 127); OLA_ASSERT_EQ(expected_data, m_received_data); // De-regsiter OLA_ASSERT_TRUE(m_osc_node->RegisterAddress(TEST_OSC_ADDRESS, NULL)); // De-register a second time OLA_ASSERT_TRUE(m_osc_node->RegisterAddress(TEST_OSC_ADDRESS, NULL)); }
/* * 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; }