diff --git a/modules/juce_blocks_basics/juce_blocks_basics.cpp b/modules/juce_blocks_basics/juce_blocks_basics.cpp index e8681d4a84..d833b3f1db 100644 --- a/modules/juce_blocks_basics/juce_blocks_basics.cpp +++ b/modules/juce_blocks_basics/juce_blocks_basics.cpp @@ -44,6 +44,7 @@ namespace juce #include "blocks/juce_BlockConfigManager.h" #include "blocks/juce_Block.cpp" #include "blocks/juce_BlocksVersion.cpp" +#include "topology/juce_BlockGraph.cpp" #include "topology/juce_PhysicalTopologySource.cpp" #include "topology/juce_RuleBasedTopologySource.cpp" #include "visualisers/juce_DrumPadLEDProgram.cpp" diff --git a/modules/juce_blocks_basics/juce_blocks_basics.h b/modules/juce_blocks_basics/juce_blocks_basics.h index 131d0f13ae..7bd008822f 100644 --- a/modules/juce_blocks_basics/juce_blocks_basics.h +++ b/modules/juce_blocks_basics/juce_blocks_basics.h @@ -75,6 +75,7 @@ namespace juce #include "blocks/juce_StatusLight.h" #include "blocks/juce_BlocksVersion.h" #include "topology/juce_Topology.h" +#include "topology/juce_BlockGraph.h" #include "topology/juce_TopologySource.h" #include "topology/juce_PhysicalTopologySource.h" #include "topology/juce_RuleBasedTopologySource.h" diff --git a/modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp b/modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp index 1b0be1c8cb..4fe9cb91d8 100644 --- a/modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp +++ b/modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp @@ -490,15 +490,14 @@ private: return getPing (uid) != nullptr; } - void forceApiDisconnected (Block::UID /*uid*/) + void forceApiDisconnected (Block::UID uid) { Array toRemove; - for (const auto& ping : blockPings) - toRemove.add (ping.blockUID); + for (auto dependentUID : detector.getDnaDependentDeviceUIDs (uid)) + removeDevice (dependentUID); - for (const auto& uid : toRemove) - removeDevice (uid); + removeDevice (uid); scheduleNewTopologyRequest(); } @@ -589,6 +588,8 @@ private: //============================================================================== void removeDevice (Block::UID uid) { + LOG_CONNECTIVITY ("Removing device: " << uid); + if (const auto info = getDeviceInfoFromUID (uid)) detector.handleDeviceRemoved (*info); diff --git a/modules/juce_blocks_basics/topology/internal/juce_Detector.cpp b/modules/juce_blocks_basics/topology/internal/juce_Detector.cpp index 7a5867c01d..a737a8ba9b 100644 --- a/modules/juce_blocks_basics/topology/internal/juce_Detector.cpp +++ b/modules/juce_blocks_basics/topology/internal/juce_Detector.cpp @@ -217,6 +217,33 @@ struct Detector : public ReferenceCountedObject, group->handleBlockRestarting (deviceID); } + Array getDnaDependentDeviceUIDs (Block::UID uid) + { + JUCE_ASSERT_MESSAGE_THREAD + + Array dependentDeviceUIDs; + + if (auto block = getBlockImplementationWithUID (uid)) + { + if (auto master = getBlockImplementationWithUID (block->masterUID)) + { + auto graph = BlockGraph (currentTopology, [uid] (Block::Ptr b) { return b->uid != uid; }); + const auto pathWithoutBlock = graph.getTraversalPathFromMaster (master); + + for (const auto b : currentTopology.blocks) + { + if (b->uid != uid && ! pathWithoutBlock.contains (b)) + { + TOPOLOGY_LOG ( "Dependent device: " + b->name); + dependentDeviceUIDs.add (b->uid); + } + } + } + } + + return dependentDeviceUIDs; + } + void handleSharedDataACK (Block::UID deviceID, uint32 packetCounter) const { JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED diff --git a/modules/juce_blocks_basics/topology/juce_BlockGraph.cpp b/modules/juce_blocks_basics/topology/juce_BlockGraph.cpp new file mode 100644 index 0000000000..6293279a87 --- /dev/null +++ b/modules/juce_blocks_basics/topology/juce_BlockGraph.cpp @@ -0,0 +1,112 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2019 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +BlockGraph::BlockGraph (const BlockTopology t, std::function filterIn) + : topology (t), filter (std::move (filterIn)) +{ + buildGraph(); +} + +BlockGraph::BlockGraph (BlockGraph&& other) +{ + traversalPaths = std::move (other.traversalPaths); + topology = std::move (other.topology); + filter = std::move (other.filter); +} + +BlockGraph::BlockTraversalPaths BlockGraph::getTraversalPaths() const +{ + return traversalPaths; +} + +Block::Array BlockGraph::getTraversalPathFromMaster (Block::Ptr masterBlock) const +{ + for (const auto& path : traversalPaths) + { + if (! path.isEmpty() && path[0] == masterBlock) + return path; + } + + return {}; +} + +String BlockGraph::asString() const +{ + String outputString = "Traversal Path(s):"; + + for (const auto& path : traversalPaths) + { + outputString += "\n[master]-->"; + + for (auto& block : path) + outputString += block->serialNumber + "-->"; + + outputString += "[last]"; + } + + return outputString; +} + +void BlockGraph::buildGraph() +{ + traversalPaths.clear(); + + for (const auto block : topology.blocks) + { + if (block->isMasterBlock() && shouldIncludeBlock (block)) + traversalPaths.add (buildPathFromMaster (block)); + } +} + +bool BlockGraph::shouldIncludeBlock (Block::Ptr block) const +{ + if (filter == nullptr) + return true; + + return filter (block); +} + +Block::Array BlockGraph::buildPathFromMaster (Block::Ptr masterBlock) +{ + jassert (masterBlock->isMasterBlock()); + + Block::Array orderedBlockList; + addAllConnectedToArray (masterBlock, orderedBlockList); + + return orderedBlockList; +} + +void BlockGraph::addAllConnectedToArray (Block::Ptr startBlock, Block::Array& store) +{ + store.addIfNotAlreadyThere (startBlock); + + for (const auto block : topology.getDirectlyConnectedBlocks (startBlock->uid)) + { + if (shouldIncludeBlock (block) && store.addIfNotAlreadyThere (block)) + addAllConnectedToArray (block, store); + } +} + +} // namespace juce diff --git a/modules/juce_blocks_basics/topology/juce_BlockGraph.h b/modules/juce_blocks_basics/topology/juce_BlockGraph.h new file mode 100644 index 0000000000..7509c029d2 --- /dev/null +++ b/modules/juce_blocks_basics/topology/juce_BlockGraph.h @@ -0,0 +1,64 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2019 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#pragma once + +namespace juce +{ + +/** + Represents traversal paths from master blocks and any connected blocks. +*/ +class BlockGraph +{ +public: + /** Creates a BlockGraph object from a BlockTopology with an optional filter fucntion. This + will build a block graph of traversal paths for each master. + */ + BlockGraph (const BlockTopology topology, std::function filter = nullptr); + BlockGraph (BlockGraph&&); + + using BlockTraversalPaths = Array; + + /** Get traversal paths for each master block in a topology */ + BlockTraversalPaths getTraversalPaths() const; + + /** Get traversal path for a specific master block in a topology */ + Block::Array getTraversalPathFromMaster (Block::Ptr masterBlock) const; + + /** Gets a string representation of all traversal paths */ + String asString() const; + +private: + void buildGraph(); + Block::Array buildPathFromMaster (Block::Ptr masterBlock); + void addAllConnectedToArray (Block::Ptr startBlock, Block::Array& store); + bool shouldIncludeBlock (Block::Ptr block) const; + + BlockTraversalPaths traversalPaths; // one path for each master block + BlockTopology topology; + std::function filter; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockGraph) +}; + +} // namespace juce diff --git a/modules/juce_blocks_basics/topology/juce_Topology.h b/modules/juce_blocks_basics/topology/juce_Topology.h index 3fcebbc67f..5d3bc35a25 100644 --- a/modules/juce_blocks_basics/topology/juce_Topology.h +++ b/modules/juce_blocks_basics/topology/juce_Topology.h @@ -66,6 +66,55 @@ struct BlockTopology return {}; } + + Block::Array getDirectlyConnectedBlocks (Block::UID blockUID) const + { + Block::Array connectedBlocks; + + for (const auto& connection : connections) + { + auto connectedDeviceUID = Block::UID { 0 }; + + if (connection.device1 == blockUID) + connectedDeviceUID = connection.device2; + + else if (connection.device2 == blockUID) + connectedDeviceUID = connection.device1; + + if (connectedDeviceUID != Block::UID { 0 }) + if (auto newBlock = getBlockWithUID (connectedDeviceUID)) + connectedBlocks.addIfNotAlreadyThere (newBlock); + } + + return connectedBlocks; + } + + Array getConnectionsBetweenBlocks (Block::UID uid1, Block::UID uid2) const + { + Array blockConnections; + + for (const auto& connection : connections) + { + if ((connection.device1 == uid1 && connection.device2 == uid2) + || (connection.device1 == uid2 && connection.device2 == uid1)) + { + blockConnections.add (connection); + } + } + + return {}; + } + + int getNumberOfConnectionsToBlock (Block::UID uid) const + { + int connectionCount = 0; + + for (const auto& connection : connections) + if (connection.device1 == uid || connection.device2 == uid) + ++connectionCount; + + return connectionCount; + } }; } // namespace juce