/* * Send a Discovery Unique Branch request. */ void DiscoveryAgent::SendDiscovery() { if (m_uid_ranges.empty()) { // we're hit the end of the stack, now we're done if (m_on_complete) { m_on_complete->Run(!m_tree_corrupt, m_uids); m_on_complete = NULL; } else { OLA_WARN << "Discovery complete but no callback"; } return; } UIDRange *range = m_uid_ranges.top(); if (range->uids_discovered == 0) { range->attempt++; } if (range->failures == MAX_BRANCH_FAILURES || range->attempt == MAX_EMPTY_BRANCH_ATTEMPTS || range->branch_corrupt) { // limit reached, move on to the next branch OLA_DEBUG << "Hit failure limit for (" << range->lower << ", " << range->upper << ")"; if (range->parent) range->parent->branch_corrupt = true; FreeCurrentRange(); SendDiscovery(); } else { OLA_DEBUG << "DUB " << range->lower << " - " << range->upper << ", attempt " << range->attempt << ", uids found: " << range->uids_discovered << ", failures " << range->failures << ", corrupted " << range->branch_corrupt; m_target->Branch(range->lower, range->upper, m_branch_callback.get()); } }
/* * Start the discovery process * @param on_complete the callback to run when discovery completes * @param incremental true if this is incremental, false otherwise */ void DiscoveryAgent::InitDiscovery( DiscoveryCompleteCallback *on_complete, bool incremental) { if (m_on_complete) { OLA_WARN << "Discovery procedure already running"; UIDSet uids; on_complete->Run(false, uids); return; } m_on_complete = on_complete; // this should be empty, but clear it out anyway while (!m_uids_to_mute.empty()) { m_uids_to_mute.pop(); } // this should also be empty while (!m_uid_ranges.empty()) { FreeCurrentRange(); } if (incremental) { UIDSet::Iterator iter = m_uids.Begin(); for (; iter != m_uids.End(); ++iter) { m_uids_to_mute.push(*iter); } } else { m_uids.Clear(); } m_bad_uids.Clear(); m_tree_corrupt = false; // push the first range on to the branch stack UID lower(0, 0); m_uid_ranges.push(new UIDRange(lower, UID::AllDevices(), NULL)); m_unmute_count = 0; m_target->UnMuteAll(m_unmute_callback.get()); }
/* * Handle a DUB response (inc. timeouts). * @param data the raw response, excluding the start code * @param length the length of the response, 0 if no response was received. */ void DiscoveryAgent::BranchComplete(const uint8_t *data, unsigned int length) { OLA_INFO << "BranchComplete, got " << length; if (length == 0) { // timeout if (!m_uid_ranges.empty()) { FreeCurrentRange(); } SendDiscovery(); return; } // Must at least have the separator, the EUID and the checksum if (length < 1 + EUID_SIZE + CHECKSUM_SIZE) { HandleCollision(); return; } unsigned int offset = 0; while (data[offset] != PREAMBLE_SEPARATOR && offset < PREAMBLE_SIZE - 1) { if (data[offset] != PREAMBLE) { OLA_INFO << "Preamble " << offset << " " << strings::ToHex(data[offset]); HandleCollision(); return; } offset++; } if (data[offset] != PREAMBLE_SEPARATOR) { OLA_INFO << "Preamble separator" << offset << " " << strings::ToHex(data[offset]); HandleCollision(); return; } offset++; unsigned int remaining = length - offset; if (remaining < EUID_SIZE + CHECKSUM_SIZE) { OLA_INFO << "Insufficient data remaining, was " << remaining; HandleCollision(); return; } typedef struct { uint8_t euid11; uint8_t euid10; uint8_t euid9; uint8_t euid8; uint8_t euid7; uint8_t euid6; uint8_t euid5; uint8_t euid4; uint8_t euid3; uint8_t euid2; uint8_t euid1; uint8_t euid0; uint8_t ecs3; uint8_t ecs2; uint8_t ecs1; uint8_t ecs0; } dub_response_structure; const dub_response_structure *response = reinterpret_cast<const dub_response_structure*>(data + offset); uint16_t calculated_checksum = 0; for (unsigned int i = offset; i < offset + EUID_SIZE; i++) { calculated_checksum += data[i]; } uint16_t recovered_checksum = JoinUInt8((response->ecs3 & response->ecs2), (response->ecs1 & response->ecs0)); if (recovered_checksum != calculated_checksum) { OLA_INFO << "Recovered checksum: " << recovered_checksum << " != " << "calculated checksum: " << calculated_checksum; HandleCollision(); return; } // ok this is a valid response uint16_t manufacturer_id = JoinUInt8((response->euid11 & response->euid10), (response->euid9 & response->euid8)); uint32_t device_id = JoinUInt8((response->euid7 & response->euid6), (response->euid5 & response->euid4), (response->euid3 & response->euid2), (response->euid1 & response->euid0)); UIDRange *range = m_uid_ranges.top(); // we store this as an instance variable so we don't have to create a new // callback each time. UID located_uid = UID(manufacturer_id, device_id); if (m_uids.Contains(located_uid)) { OLA_WARN << "Previous muted responder " << located_uid << " continues to respond"; range->failures++; // ignore this and continue on to the next branch. SendDiscovery(); } else if (m_bad_uids.Contains(located_uid)) { // we've already tried this one range->failures++; SendDiscovery(); } else { m_muting_uid = located_uid; m_mute_attempts = 0; OLA_INFO << "Muting " << m_muting_uid; m_target->MuteDevice(m_muting_uid, m_branch_mute_callback.get()); } }
/** * Called when we get a response (or timeout) to a branch request. * @param data the raw response, excluding the start code * @param length the length of the response, 0 if no response was received. */ void DiscoveryAgent::BranchComplete(const uint8_t *data, unsigned int length) { if (length == 0) { // timeout FreeCurrentRange(); SendDiscovery(); return; } if (length < MIN_DUB_RESPONSE_SIZE || length > MAX_DUB_RESPONSE_SIZE) { HandleCollision(); return; } unsigned int preamble_size = length - MIN_DUB_RESPONSE_SIZE; for (unsigned int i = 0; i < preamble_size; i++) { if (data[i] != 0xfe) { OLA_INFO << "preamble " << i << " " << std::hex << static_cast<int>(data[i]); HandleCollision(); return; } } unsigned int offset = preamble_size; if (data[offset++] != 0xaa) { OLA_INFO << "preamble separator is " << std::hex << static_cast<int>(data[offset]); HandleCollision(); return; } typedef struct { uint8_t euid11; uint8_t euid10; uint8_t euid9; uint8_t euid8; uint8_t euid7; uint8_t euid6; uint8_t euid5; uint8_t euid4; uint8_t euid3; uint8_t euid2; uint8_t euid1; uint8_t euid0; uint8_t ecs3; uint8_t ecs2; uint8_t ecs1; uint8_t ecs0; } dub_response_structure; const dub_response_structure *response = reinterpret_cast<const dub_response_structure*>(data + offset); uint16_t calculated_checksum = 0; for (unsigned int i = offset; i < offset + 12; i++) calculated_checksum += data[i]; uint16_t recovered_checksum = ((response->ecs3 & response->ecs2) << 8) + (response->ecs1 & response->ecs0); if (recovered_checksum != calculated_checksum) { OLA_INFO << "recovered checksum: " << recovered_checksum << " != " << "calculated checksum: " << calculated_checksum; HandleCollision(); return; } // ok this is a valid response uint16_t manufacturer_id = ((response->euid11 & response->euid10) << 8) + (response->euid9 & response->euid8); uint32_t device_id = ((response->euid7 & response->euid6) << 24) + ((response->euid5 & response->euid4) << 16) + ((response->euid3 & response->euid2) << 8) + (response->euid1 & response->euid0); UIDRange *range = m_uid_ranges.top(); // we store this as an instance variable so we don't have to create a new // callback each time. UID located_uid = UID(manufacturer_id, device_id); if (m_uids.Contains(located_uid)) { OLA_WARN << "Previous muted responder " << located_uid << " continues to respond"; range->failures++; // ignore this and continue on to the next branch. SendDiscovery(); } else if (m_bad_uids.Contains(located_uid)) { // we've already tried this one range->failures++; SendDiscovery(); } else { m_muting_uid = located_uid; m_mute_attempts = 0; OLA_INFO << "muting " << m_muting_uid; m_target->MuteDevice(m_muting_uid, m_branch_mute_callback); } }