| @@ -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" | |||
| @@ -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" | |||
| @@ -490,15 +490,14 @@ private: | |||
| return getPing (uid) != nullptr; | |||
| } | |||
| void forceApiDisconnected (Block::UID /*uid*/) | |||
| void forceApiDisconnected (Block::UID uid) | |||
| { | |||
| Array<Block::UID> 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); | |||
| @@ -217,6 +217,33 @@ struct Detector : public ReferenceCountedObject, | |||
| group->handleBlockRestarting (deviceID); | |||
| } | |||
| Array<Block::UID> getDnaDependentDeviceUIDs (Block::UID uid) | |||
| { | |||
| JUCE_ASSERT_MESSAGE_THREAD | |||
| Array<Block::UID> 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 | |||
| @@ -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<bool(Block::Ptr)> 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 | |||
| @@ -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<bool(Block::Ptr block)> filter = nullptr); | |||
| BlockGraph (BlockGraph&&); | |||
| using BlockTraversalPaths = Array<Block::Array, CriticalSection>; | |||
| /** 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<bool(Block::Ptr)> filter; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockGraph) | |||
| }; | |||
| } // namespace juce | |||
| @@ -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<BlockDeviceConnection> getConnectionsBetweenBlocks (Block::UID uid1, Block::UID uid2) const | |||
| { | |||
| Array<BlockDeviceConnection> 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 | |||