Browse Source

BLOCKS: Added callbacks from the topology for device added, removed and updated

tags/2021-05-28
ed 6 years ago
parent
commit
7cf52297ee
11 changed files with 642 additions and 580 deletions
  1. +4
    -4
      modules/juce_blocks_basics/blocks/juce_Block.cpp
  2. +7
    -0
      modules/juce_blocks_basics/blocks/juce_Block.h
  3. +53
    -31
      modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h
  4. +5
    -2
      modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h
  5. +40
    -33
      modules/juce_blocks_basics/topology/internal/juce_BlockImplementation.cpp
  6. +276
    -300
      modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp
  7. +16
    -5
      modules/juce_blocks_basics/topology/internal/juce_DepreciatedVersionReader.cpp
  8. +217
    -190
      modules/juce_blocks_basics/topology/internal/juce_Detector.cpp
  9. +10
    -3
      modules/juce_blocks_basics/topology/internal/juce_DeviceInfo.cpp
  10. +0
    -11
      modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp
  11. +14
    -1
      modules/juce_blocks_basics/topology/juce_TopologySource.h

+ 4
- 4
modules/juce_blocks_basics/blocks/juce_Block.cpp View File

@@ -27,7 +27,7 @@ static Block::UID getBlockUIDFromSerialNumber (const uint8* serial) noexcept
{
Block::UID n = {};
for (int i = 0; i < (int) sizeof (BlocksProtocol::BlockSerialNumber); ++i)
for (int i = 0; i < int (BlocksProtocol::BlockSerialNumber::maxLength); ++i)
n += n * 127 + serial[i];
return n;
@@ -35,15 +35,15 @@ static Block::UID getBlockUIDFromSerialNumber (const uint8* serial) noexcept
static Block::UID getBlockUIDFromSerialNumber (const BlocksProtocol::BlockSerialNumber& serial) noexcept
{
return getBlockUIDFromSerialNumber (serial.serial);
return getBlockUIDFromSerialNumber (serial.data);
}
static Block::UID getBlockUIDFromSerialNumber (const juce::String& serial) noexcept
{
if (serial.length() < (int) sizeof (BlocksProtocol::BlockSerialNumber))
if (serial.length() < int (BlocksProtocol::BlockSerialNumber::maxLength))
{
jassertfalse;
return getBlockUIDFromSerialNumber (serial.paddedRight ('0', sizeof (BlocksProtocol::BlockSerialNumber)));
return getBlockUIDFromSerialNumber (serial.paddedRight ('0', BlocksProtocol::BlockSerialNumber::maxLength));
}
return getBlockUIDFromSerialNumber ((const uint8*) serial.toRawUTF8());


+ 7
- 0
modules/juce_blocks_basics/blocks/juce_Block.h View File

@@ -107,6 +107,13 @@ public:
/** Returns true if this block is connected and active. */
virtual bool isConnected() const = 0;
/** Returns the time this block object was connected to the topology.
Only valid when isConnected == true.
@see isConnected
*/
virtual Time getConnectionTime() const = 0;
/** Returns true if this block is directly connected to the application,
as opposed to only being connected to a different block via a connection port.


+ 53
- 31
modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h View File

@@ -123,36 +123,6 @@ using BatteryCharging = IntegerWithBitSize<1>;
*/
using ConnectorPort = IntegerWithBitSize<5>;
//==============================================================================
/** Structure describing a block's serial number
@tags{Blocks}
*/
struct BlockSerialNumber
{
uint8 serial[16];
bool isValid() const noexcept
{
for (auto c : serial)
if (c == 0)
return false;
return isAnyControlBlock() || isPadBlock() || isSeaboardBlock();
}
bool isPadBlock() const noexcept { return hasPrefix ("LPB") || hasPrefix ("LPM"); }
bool isLiveBlock() const noexcept { return hasPrefix ("LIC"); }
bool isLoopBlock() const noexcept { return hasPrefix ("LOC"); }
bool isDevCtrlBlock() const noexcept { return hasPrefix ("DCB"); }
bool isTouchBlock() const noexcept { return hasPrefix ("TCB"); }
bool isSeaboardBlock() const noexcept { return hasPrefix ("SBB"); }
bool isAnyControlBlock() const noexcept { return isLiveBlock() || isLoopBlock() || isDevCtrlBlock() || isTouchBlock(); }
bool hasPrefix (const char* prefix) const noexcept { return memcmp (serial, prefix, 3) == 0; }
};
//==============================================================================
/** Structure for generic block data
@@ -171,6 +141,11 @@ struct BlockStringData
return length > 0;
}
juce::String asString() const
{
return juce::String ((const char*) data, length);
}
bool operator== (const BlockStringData& other) const
{
if (length != other.length)
@@ -192,6 +167,34 @@ struct BlockStringData
using VersionNumber = BlockStringData<21>;
using BlockName = BlockStringData<33>;
//==============================================================================
/** Structure describing a block's serial number
@tags{Blocks}
*/
struct BlockSerialNumber : public BlockStringData<16>
{
bool isValid() const noexcept
{
for (auto c : data)
if (c == 0)
return false;
return isAnyControlBlock() || isPadBlock() || isSeaboardBlock();
}
bool isPadBlock() const noexcept { return hasPrefix ("LPB") || hasPrefix ("LPM"); }
bool isLiveBlock() const noexcept { return hasPrefix ("LIC"); }
bool isLoopBlock() const noexcept { return hasPrefix ("LOC"); }
bool isDevCtrlBlock() const noexcept { return hasPrefix ("DCB"); }
bool isTouchBlock() const noexcept { return hasPrefix ("TCB"); }
bool isSeaboardBlock() const noexcept { return hasPrefix ("SBB"); }
bool isAnyControlBlock() const noexcept { return isLiveBlock() || isLoopBlock() || isDevCtrlBlock() || isTouchBlock(); }
bool hasPrefix (const char* prefix) const noexcept { return memcmp (data, prefix, 3) == 0; }
};
//==============================================================================
/** Structure for the device status
@@ -214,6 +217,25 @@ struct DeviceConnection
{
TopologyIndex device1, device2;
ConnectorPort port1, port2;
bool operator== (const DeviceConnection& other) const
{
return isEqual (other);
}
bool operator!= (const DeviceConnection& other) const
{
return ! isEqual (other);
}
private:
bool isEqual (const DeviceConnection& other) const
{
return device1 == other.device1
&& device2 == other.device2
&& port1 == other.port1
&& port2 == other.port2;
}
};
//==============================================================================
@@ -445,7 +467,7 @@ static constexpr uint32 controlBlockStackSize = 800;
enum BitSizes
{
topologyMessageHeader = MessageType::bits + ProtocolVersion::bits + DeviceCount::bits + ConnectionCount::bits,
topologyDeviceInfo = sizeof (BlockSerialNumber) * 7 + BatteryLevel::bits + BatteryCharging::bits,
topologyDeviceInfo = BlockSerialNumber::maxLength * 7 + BatteryLevel::bits + BatteryCharging::bits,
topologyConnectionInfo = topologyIndexBits + ConnectorPort::bits + topologyIndexBits + ConnectorPort::bits,
typeDeviceAndTime = MessageType::bits + PacketTimestampOffset::bits,


+ 5
- 2
modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h View File

@@ -164,8 +164,11 @@ struct HostPacketDecoder
{
DeviceStatus status;
for (uint32 i = 0; i < sizeof (BlockSerialNumber); ++i)
status.serialNumber.serial[i] = (uint8) reader.readBits (7);
for (uint32 i = 0; i < BlockSerialNumber::maxLength; ++i)
{
status.serialNumber.data[i] = (uint8) reader.readBits (7);
++status.serialNumber.length;
}
status.index = (TopologyIndex) reader.readBits (topologyIndexBits);
status.batteryLevel = reader.read<BatteryLevel>();


+ 40
- 33
modules/juce_blocks_basics/topology/internal/juce_BlockImplementation.cpp View File

@@ -34,19 +34,16 @@ public:
struct LEDGridImplementation;
struct LEDRowImplementation;
BlockImplementation (const BlocksProtocol::BlockSerialNumber& serial,
Detector& detectorToUse,
BlocksProtocol::VersionNumber version,
BlocksProtocol::BlockName blockName,
bool isMasterBlock)
: Block (juce::String ((const char*) serial.serial, sizeof (serial.serial)),
juce::String ((const char*) version.data, version.length),
juce::String ((const char*) blockName.data, blockName.length)),
modelData (serial),
BlockImplementation (Detector& detectorToUse, const DeviceInfo& deviceInfo)
: Block (deviceInfo.serial.asString(),
deviceInfo.version.asString(),
deviceInfo.name.asString()),
modelData (deviceInfo.serial),
remoteHeap (modelData.programAndHeapSize),
detector (&detectorToUse),
isMaster (isMasterBlock)
detector (&detectorToUse)
{
markReconnected (deviceInfo);
if (modelData.hasTouchSurface)
touchSurface.reset (new TouchSurfaceImplementation (*this));
@@ -77,13 +74,25 @@ public:
{
if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get()))
surface->disableTouchSurface();
connectionTime = Time();
}
void markReconnected (const DeviceInfo& deviceInfo)
{
versionNumber = asString (deviceInfo.version);
name = asString (deviceInfo.name);
if (wasPowerCycled())
resetPowerCycleFlag();
if (connectionTime == Time())
connectionTime = Time::getCurrentTime();
versionNumber = deviceInfo.version.asString();
name = deviceInfo.name.asString();
isMaster = deviceInfo.isMaster;
masterUID = deviceInfo.masterUid;
batteryCharging = deviceInfo.batteryCharging;
batteryLevel = deviceInfo.batteryLevel;
topologyIndex = deviceInfo.index;
setProgram (nullptr);
remoteHeap.resetDeviceStateToUnknown();
@@ -120,6 +129,7 @@ public:
bool isHardwareBlock() const override { return true; }
juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; }
bool isConnected() const override { return detector && detector->isConnected (uid); }
juce::Time getConnectionTime() const override { return connectionTime; }
bool isMasterBlock() const override { return isMaster; }
Block::UID getConnectedMasterUID() const override { return masterUID; }
int getRotation() const override { return rotation; }
@@ -159,24 +169,12 @@ public:
float getBatteryLevel() const override
{
if (detector == nullptr)
return 0.0f;
if (auto status = detector->getLastStatus (uid))
return status->batteryLevel.toUnipolarFloat();
return 0.0f;
return batteryLevel.toUnipolarFloat();
}
bool isBatteryCharging() const override
{
if (detector == nullptr)
return false;
if (auto status = detector->getLastStatus (uid))
return status->batteryCharging.get() != 0;
return false;
return batteryCharging.get() > 0;
}
bool supportsGraphics() const override
@@ -186,10 +184,7 @@ public:
int getDeviceIndex() const noexcept
{
if (detector == nullptr)
return -1;
return isConnected() ? detector->getIndexFromDeviceID (uid) : -1;
return isConnected() ? topologyIndex : -1;
}
template <typename PacketBuilder>
@@ -220,10 +215,15 @@ public:
programEventListeners.call ([&] (ProgramEventListener& l) { l.handleProgramEvent (*this, m); });
}
static BlockImplementation* getFrom (Block* b) noexcept
{
jassert (dynamic_cast<BlockImplementation*> (b) != nullptr);
return dynamic_cast<BlockImplementation*> (b);
}
static BlockImplementation* getFrom (Block& b) noexcept
{
jassert (dynamic_cast<BlockImplementation*> (&b) != nullptr);
return dynamic_cast<BlockImplementation*> (&b);
return getFrom (&b);
}
//==============================================================================
@@ -584,6 +584,13 @@ private:
bool isMaster = false;
Block::UID masterUID = {};
BlocksProtocol::BatteryLevel batteryLevel {};
BlocksProtocol::BatteryCharging batteryCharging {};
BlocksProtocol::TopologyIndex topologyIndex {};
Time connectionTime {};
std::pair<int, int> position;
int rotation = 0;
friend Detector;


+ 276
- 300
modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp View File

@@ -29,55 +29,6 @@ namespace
{
return static_cast<Block::Timestamp> (timestamp);
}
template <typename V>
static int removeUnusedBlocksFromMap (std::map<Block::UID, V>& mapToClean, const juce::Array<DeviceInfo>& currentDevices)
{
int numRemoved = 0;
for (auto iter = mapToClean.begin(); iter != mapToClean.end();)
{
bool found = false;
for (auto& info : currentDevices)
{
if (info.uid == iter->first)
{
found = true;
break;
}
}
if (found)
{
++iter;
}
else
{
mapToClean.erase (iter++);
++numRemoved;
}
}
return numRemoved;
}
/* Returns true new element added to map or value of existing key changed */
template <typename V>
static bool insertOrAssign (std::map<Block::UID, V>& map, const Block::UID& key, const V& value)
{
const auto result = map.emplace (std::make_pair (key, value));
if (! result.second)
{
if (result.first->second == value)
return false;
result.first->second = value;
}
return true;
}
}
template <typename Detector>
@@ -88,8 +39,6 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
ConnectedDeviceGroup (Detector& d, const juce::String& name, PhysicalTopologySource::DeviceConnection* connection)
: detector (d), deviceName (name), deviceConnection (connection)
{
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
{
depreciatedVersionReader = std::make_unique<DepreciatedVersionReader> (*midiDeviceConnection);
@@ -106,30 +55,23 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
sendTopologyRequest();
}
bool isStillConnected (const juce::StringArray& detectedDevices) const noexcept
~ConnectedDeviceGroup()
{
return detectedDevices.contains (deviceName) && ! failedToGetTopology();
for (const auto& device : currentDeviceInfo)
detector.handleDeviceRemoved (device);
}
int getIndexFromDeviceID (Block::UID uid) const noexcept
bool isStillConnected (const juce::StringArray& detectedDevices) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return d.index;
return -1;
return detectedDevices.contains (deviceName) && ! failedToGetTopology();
}
const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
bool contains (Block::UID uid)
{
for (auto&& status : currentTopologyDevices)
if (getBlockUIDFromSerialNumber (status.serialNumber) == deviceID)
return &status;
return nullptr;
return getIndexFromDeviceID (uid) >= 0;
}
void notifyBlockIsRestarting (Block::UID deviceID)
void handleBlockRestarting (Block::UID deviceID)
{
forceApiDisconnected (deviceID);
}
@@ -164,63 +106,52 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
{
lastTopologyReceiveTime = juce::Time::getCurrentTime();
if (incomingTopologyDevices.isEmpty())
if (incomingTopologyDevices.isEmpty()
|| incomingTopologyConnections.size() < incomingTopologyDevices.size() - 1)
{
jassertfalse;
LOG_CONNECTIVITY ("Invalid topology or device list received.");
LOG_CONNECTIVITY ("Device size : " << incomingTopologyDevices.size());
LOG_CONNECTIVITY ("Connections size : " << incomingTopologyConnections.size());
scheduleNewTopologyRequest();
return;
}
currentTopologyDevices.swapWith (incomingTopologyDevices);
currentTopologyConnections.swapWith (incomingTopologyConnections);
incomingTopologyDevices.clearQuick();
incomingTopologyConnections.clearQuick();
refreshCurrentDeviceInfo();
refreshCurrentDeviceConnections();
removeUnusedBlocksFromMap (versionNumbers, currentDeviceInfo);
removeUnusedBlocksFromMap (blockNames, currentDeviceInfo);
LOG_CONNECTIVITY ("Valid topology received");
removePingForDisconnectedBlocks();
detector.handleTopologyChange();
updateCurrentDeviceList();
updateCurrentDeviceConnections();
}
void handleVersion (BlocksProtocol::DeviceVersion version)
{
const auto uid = getDeviceIDFromIndex (version.index);
if (uid == Block::UID() || version.version.length <= 1)
return;
setVersion (uid, version.version);
setVersion (version.index, version.version);
}
void handleName (BlocksProtocol::DeviceName name)
{
const auto uid = getDeviceIDFromIndex (name.index);
if (uid == Block::UID() || name.name.length <= 1)
if (name.name.length <= 1)
return;
if (insertOrAssign (blockNames, uid, name.name))
if (const auto info = getDeviceInfoFromIndex (name.index))
{
refreshCurrentDeviceInfo();
detector.handleTopologyChange();
if (info->name == name.name)
return;
info->name = name.name;
detector.handleDeviceUpdated (*info);
}
}
void handleControlButtonUpDown (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp,
BlocksProtocol::ControlButtonID buttonID, bool isDown)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleButtonChange (deviceID, deviceTimestampToHost (timestamp), buttonID.get(), isDown);
}
void handleCustomMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp, const int32* data)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleCustomMessage (deviceID, deviceTimestampToHost (timestamp), data);
}
@@ -231,7 +162,7 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
BlocksProtocol::TouchVelocity velocity,
bool isStart, bool isEnd)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
{
TouchSurface::Touch touch;
@@ -267,7 +198,7 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
void handlePacketACK (BlocksProtocol::TopologyIndex deviceIndex,
BlocksProtocol::PacketCounter counter)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
{
detector.handleSharedDataACK (deviceID, counter);
updateApiPing (deviceID);
@@ -278,7 +209,7 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
BlocksProtocol::FirmwareUpdateACKCode resultCode,
BlocksProtocol::FirmwareUpdateACKDetail resultDetail)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
{
detector.handleFirmwareUpdateACK (deviceID, (uint8) resultCode.get(), (uint32) resultDetail.get());
updateApiPing (deviceID);
@@ -288,32 +219,32 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
void handleConfigUpdateMessage (BlocksProtocol::TopologyIndex deviceIndex,
int32 item, int32 value, int32 min, int32 max)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigUpdateMessage (deviceID, item, value, min, max);
}
void handleConfigSetMessage (BlocksProtocol::TopologyIndex deviceIndex,
int32 item, int32 value)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigSetMessage (deviceID, item, value);
}
void handleConfigFactorySyncEndMessage (BlocksProtocol::TopologyIndex deviceIndex)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigFactorySyncEndMessage (deviceID);
}
void handleConfigFactorySyncResetMessage (BlocksProtocol::TopologyIndex deviceIndex)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleConfigFactorySyncResetMessage (deviceID);
}
void handleLogMessage (BlocksProtocol::TopologyIndex deviceIndex, const String& message)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
if (auto deviceID = getDeviceIDFromIndex (deviceIndex))
detector.handleLogMessage (deviceID, message);
}
@@ -337,17 +268,14 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
return deviceConnection.get();
}
juce::Array<DeviceInfo> getCurrentDeviceInfo()
{
auto blocks = currentDeviceInfo;
blocks.removeIf ([this] (DeviceInfo& info) { return ! isApiConnected (info.uid); });
return blocks;
}
juce::Array<BlockDeviceConnection> getCurrentDeviceConnections()
{
auto connections = currentDeviceConnections;
connections.removeIf ([this] (BlockDeviceConnection& c) { return ! isApiConnected (c.device1) || ! isApiConnected (c.device2); });
juce::Array<BlockDeviceConnection> connections;
for (const auto& connection : currentDeviceConnections)
if (isApiConnected (getDeviceIDFromIndex (connection.device1)) && isApiConnected (getDeviceIDFromIndex (connection.device2)))
connections.add (getBlockDeviceConnection (connection));
return connections;
}
@@ -359,18 +287,14 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
private:
//==============================================================================
juce::Array<DeviceInfo> currentDeviceInfo;
juce::Array<BlockDeviceConnection> currentDeviceConnections;
std::unique_ptr<PhysicalTopologySource::DeviceConnection> deviceConnection;
juce::Array<BlocksProtocol::DeviceStatus> incomingTopologyDevices;
juce::Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections, currentDeviceConnections;
juce::Array<BlocksProtocol::DeviceStatus> incomingTopologyDevices, currentTopologyDevices;
juce::Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections, currentTopologyConnections;
std::unique_ptr<PhysicalTopologySource::DeviceConnection> deviceConnection;
juce::CriticalSection incomingPacketLock;
juce::Array<juce::MemoryBlock> incomingPackets;
std::map<Block::UID, BlocksProtocol::VersionNumber> versionNumbers;
std::map<Block::UID, BlocksProtocol::BlockName> blockNames;
std::unique_ptr<DepreciatedVersionReader> depreciatedVersionReader;
struct TouchStart { float x, y; };
@@ -378,6 +302,21 @@ private:
Block::UID masterBlock = 0;
//==============================================================================
void timerCallback() override
{
const auto now = juce::Time::getCurrentTime();
if ((now > lastTopologyReceiveTime + juce::RelativeTime::seconds (30.0))
&& now > lastTopologyRequestTime + juce::RelativeTime::seconds (1.0)
&& numTopologyRequestsSent < 4)
sendTopologyRequest();
checkApiTimeouts (now);
startApiModeOnConnectedBlocks();
requestMasterBlockVersionIfNeeded();
}
//==============================================================================
void setMidiMessageCallback()
{
@@ -387,12 +326,57 @@ private:
};
}
void handleIncomingMessage (const void* data, size_t dataSize)
{
juce::MemoryBlock mb (data, dataSize);
{
const juce::ScopedLock sl (incomingPacketLock);
incomingPackets.add (std::move (mb));
}
triggerAsyncUpdate();
#if DUMP_BANDWIDTH_STATS
registerBytesIn ((int) dataSize);
#endif
}
void handleAsyncUpdate() override
{
juce::Array<juce::MemoryBlock> packets;
packets.ensureStorageAllocated (32);
{
const juce::ScopedLock sl (incomingPacketLock);
incomingPackets.swapWith (packets);
}
for (auto& packet : packets)
{
auto data = static_cast<const uint8*> (packet.getData());
BlocksProtocol::HostPacketDecoder<ConnectedDeviceGroup>
::processNextPacket (*this, *data, data + 1, (int) packet.getSize() - 1);
}
}
bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
{
BlocksProtocol::HostPacketBuilder<64> p;
p.writePacketSysexHeaderBytes (deviceIndex);
p.deviceControlMessage (commandID);
p.writePacketSysexFooter();
return sendMessageToDevice (p);
}
//==============================================================================
juce::Time lastTopologyRequestTime, lastTopologyReceiveTime;
int numTopologyRequestsSent = 0;
void scheduleNewTopologyRequest()
{
LOG_CONNECTIVITY ("Topology Request Scheduled");
numTopologyRequestsSent = 0;
lastTopologyReceiveTime = juce::Time();
lastTopologyRequestTime = juce::Time::getCurrentTime();
@@ -405,34 +389,11 @@ private:
sendCommandMessage (0, BlocksProtocol::requestTopologyMessage);
}
void timerCallback() override
{
const auto now = juce::Time::getCurrentTime();
if ((now > lastTopologyReceiveTime + juce::RelativeTime::seconds (30.0))
&& now > lastTopologyRequestTime + juce::RelativeTime::seconds (1.0)
&& numTopologyRequestsSent < 4)
sendTopologyRequest();
checkApiTimeouts (now);
startApiModeOnConnectedBlocks();
requestMasterBlockVersionIfNeeded();
}
bool failedToGetTopology() const noexcept
{
return numTopologyRequestsSent >= 4 && lastTopologyReceiveTime == juce::Time();
}
bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
{
BlocksProtocol::HostPacketBuilder<64> p;
p.writePacketSysexHeaderBytes (deviceIndex);
p.deviceControlMessage (commandID);
p.writePacketSysexFooter();
return sendMessageToDevice (p);
}
//==============================================================================
void requestMasterBlockVersionIfNeeded()
{
@@ -442,18 +403,31 @@ private:
const auto masterVersion = depreciatedVersionReader->getVersionNumber();
if (masterVersion.isNotEmpty())
setVersion (masterBlock, masterVersion);
{
const auto masterIndex = getIndexFromDeviceID (masterBlock);
if (masterIndex >= 0)
setVersion (BlocksProtocol::TopologyIndex (masterIndex), masterVersion);
else
jassertfalse;
}
}
void setVersion (const Block::UID uid, const BlocksProtocol::VersionNumber versionNumber)
void setVersion (const BlocksProtocol::TopologyIndex index, const BlocksProtocol::VersionNumber versionNumber)
{
if (uid == masterBlock)
depreciatedVersionReader.reset();
if (versionNumber.length <= 1)
return;
if (insertOrAssign (versionNumbers, uid, versionNumber))
if (const auto info = getDeviceInfoFromIndex (index))
{
refreshCurrentDeviceInfo();
detector.handleTopologyChange();
if (info->version == versionNumber)
return;
if (info->uid == masterBlock)
depreciatedVersionReader.reset();
info->version = versionNumber;
detector.handleDeviceUpdated (*info);
}
}
@@ -467,6 +441,31 @@ private:
juce::Array<BlockPingTime> blockPings;
BlockPingTime* getPing (Block::UID uid)
{
for (auto& ping : blockPings)
if (uid == ping.blockUID)
return &ping;
return nullptr;
}
void removePing (Block::UID uid)
{
const auto remove = [uid] (const BlockPingTime& ping)
{
if (uid == ping.blockUID)
{
LOG_CONNECTIVITY ("API Disconnected by topology update " << ping.blockUID);
return true;
}
return false;
};
blockPings.removeIf (remove);
}
void updateApiPing (Block::UID uid)
{
const auto now = juce::Time::getCurrentTime();
@@ -480,22 +479,10 @@ private:
{
LOG_CONNECTIVITY ("API Connected " << uid);
blockPings.add ({ uid, now, now });
detector.handleTopologyChange();
}
}
BlockPingTime* getPing (Block::UID uid)
{
for (auto& ping : blockPings)
if (uid == ping.blockUID)
return &ping;
return nullptr;
}
void removeDeviceInfo (Block::UID uid)
{
currentDeviceInfo.removeIf ([uid] (DeviceInfo& info) { return uid == info.uid; });
if (const auto info = getDeviceInfoFromUID (uid))
detector.handleDeviceAdded (*info);
}
}
bool isApiConnected (Block::UID uid)
@@ -503,55 +490,35 @@ private:
return getPing (uid) != nullptr;
}
void forceApiDisconnected (Block::UID uid)
void forceApiDisconnected (Block::UID /*uid*/)
{
if (isApiConnected (uid))
{
// Clear all known API connections and broadcast an empty topology,
// as DNA blocks connected to the restarting block may be offline.
LOG_CONNECTIVITY ("API Disconnected " << uid << ", re-probing topology");
currentDeviceInfo.clearQuick();
blockPings.clearQuick();
blockNames.clear();
versionNumbers.clear();
detector.handleTopologyChange();
scheduleNewTopologyRequest();
}
}
Array<Block::UID> toRemove;
void checkApiTimeouts (juce::Time now)
{
const auto timedOut = [this, now] (BlockPingTime& ping)
{
if (ping.lastPing >= now - juce::RelativeTime::seconds (pingTimeoutSeconds))
return false;
for (const auto& ping : blockPings)
toRemove.add (ping.blockUID);
LOG_CONNECTIVITY ("Ping timeout: " << ping.blockUID);
removeDeviceInfo (ping.blockUID);
return true;
};
for (const auto& uid : toRemove)
removeDevice (uid);
if (blockPings.removeIf (timedOut) > 0)
{
scheduleNewTopologyRequest();
detector.handleTopologyChange();
}
scheduleNewTopologyRequest();
}
/* Returns true is ping was removed */
void removePingForDisconnectedBlocks()
void checkApiTimeouts (juce::Time now)
{
const auto removed = [this] (auto& ping)
{
for (auto& info : currentDeviceInfo)
if (info.uid == ping.blockUID)
return false;
Array<Block::UID> toRemove;
LOG_CONNECTIVITY ("API Disconnected by topology update " << ping.blockUID);
return true;
};
for (const auto& ping : blockPings)
{
if (ping.lastPing < now - juce::RelativeTime::seconds (pingTimeoutSeconds))
{
LOG_CONNECTIVITY ("Ping timeout: " << ping.blockUID);
toRemove.add (ping.blockUID);
scheduleNewTopologyRequest();
}
}
blockPings.removeIf (removed);
for (const auto& uid : toRemove)
removeDevice (uid);
}
void startApiModeOnConnectedBlocks()
@@ -568,151 +535,160 @@ private:
}
//==============================================================================
void checkVersionNumberTimeouts()
Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) noexcept
{
for (const auto& device : currentDeviceInfo)
{
const auto version = versionNumbers.find (device.uid);
if (device.index == index)
return device.uid;
if (version == versionNumbers.end())
{
auto* ping = getPing (device.uid);
scheduleNewTopologyRequest();
return {};
}
}
}
int getIndexFromDeviceID (Block::UID uid) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return d.index;
return -1;
}
//==============================================================================
Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.index == index)
return d.uid;
if (d.uid == uid)
return &d;
return {};
return nullptr;
}
Block::UID getDeviceIDFromMessageIndex (BlocksProtocol::TopologyIndex index) noexcept
DeviceInfo* getDeviceInfoFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
{
const auto uid = getDeviceIDFromIndex (index);
for (auto& d : currentDeviceInfo)
if (d.index == index)
return &d;
// re-request topology if we get an event from an unknown block
if (uid == Block::UID())
scheduleNewTopologyRequest();
return nullptr;
}
return uid;
void removeDeviceInfo (Block::UID uid)
{
currentDeviceInfo.removeIf ([uid] (const DeviceInfo& info) { return info.uid == uid; });
}
//==============================================================================
void handleIncomingMessage (const void* data, size_t dataSize)
const DeviceStatus* getIncomingDeviceStatus (BlockSerialNumber serialNumber) const
{
juce::MemoryBlock mb (data, dataSize);
for (auto& device : incomingTopologyDevices)
if (device.serialNumber == serialNumber)
return &device;
{
const juce::ScopedLock sl (incomingPacketLock);
incomingPackets.add (std::move (mb));
}
return nullptr;
}
triggerAsyncUpdate();
//==============================================================================
void removeDevice (Block::UID uid)
{
if (const auto info = getDeviceInfoFromUID (uid))
detector.handleDeviceRemoved (*info);
#if DUMP_BANDWIDTH_STATS
registerBytesIn ((int) dataSize);
#endif
removeDeviceInfo (uid);
removePing (uid);
}
void handleAsyncUpdate() override
void updateCurrentDeviceList()
{
juce::Array<juce::MemoryBlock> packets;
packets.ensureStorageAllocated (32);
Array<Block::UID> toRemove;
//Update known devices
for (int i = currentDeviceInfo.size(); --i >= 0; )
{
const juce::ScopedLock sl (incomingPacketLock);
incomingPackets.swapWith (packets);
}
auto& currentDevice = currentDeviceInfo.getReference (i);
for (auto& packet : packets)
{
auto data = static_cast<const uint8*> (packet.getData());
if (const auto newStatus = getIncomingDeviceStatus (currentDevice.serial))
{
if (currentDevice.index != newStatus->index)
{
currentDevice.index = newStatus->index;
detector.handleIndexChanged (currentDevice.uid, currentDevice.index);
}
BlocksProtocol::HostPacketDecoder<ConnectedDeviceGroup>
::processNextPacket (*this, *data, data + 1, (int) packet.getSize() - 1);
}
}
if (currentDevice.batteryCharging != newStatus->batteryCharging)
{
currentDevice.batteryCharging = newStatus->batteryCharging;
detector.handleBatteryChargingChanged (currentDevice.uid, currentDevice.batteryCharging);
}
//==============================================================================
BlocksProtocol::VersionNumber getVersionNumber (Block::UID uid)
{
const auto version = versionNumbers.find (uid);
return version == versionNumbers.end() ? BlocksProtocol::VersionNumber() : version->second;
}
if (currentDevice.batteryLevel != newStatus->batteryLevel)
{
currentDevice.batteryLevel = newStatus->batteryLevel;
detector.handleBatteryLevelChanged (currentDevice.uid, currentDevice.batteryLevel);
}
}
else
{
toRemove.add (currentDevice.uid);
}
}
BlocksProtocol::BlockName getName (Block::UID uid)
{
const auto name = blockNames.find (uid);
return name == blockNames.end() ? BlocksProtocol::BlockName() : name->second;
}
for (const auto& uid : toRemove)
removeDevice (uid);
//==============================================================================
const DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return &d;
//Add new devices
for (const auto& device : incomingTopologyDevices)
{
const auto uid = getBlockUIDFromSerialNumber (device.serialNumber);
return nullptr;
if (! getDeviceInfoFromUID (uid))
{
// For backwards compatibility we assume the first device we see in a group is the master and won't change
if (masterBlock == 0)
masterBlock = uid;
currentDeviceInfo.add ({ uid,
device.index,
device.serialNumber,
BlocksProtocol::VersionNumber(),
BlocksProtocol::BlockName(),
device.batteryLevel,
device.batteryCharging,
masterBlock });
}
}
}
//==============================================================================
Block::ConnectionPort convertConnectionPort (Block::UID uid, BlocksProtocol::ConnectorPort p) noexcept
{
if (auto* info = getDeviceInfoFromUID (uid))
return BlocksProtocol::BlockDataSheet (info->serial).convertPortIndexToConnectorPort (p);
jassertfalse;
jassertfalse;
return { Block::ConnectionPort::DeviceEdge::north, 0 };
}
//==============================================================================
void refreshCurrentDeviceInfo()
BlockDeviceConnection getBlockDeviceConnection (const BlocksProtocol::DeviceConnection& connection)
{
currentDeviceInfo.clearQuick();
BlockDeviceConnection dc;
for (auto& device : currentTopologyDevices)
{
const auto uid = getBlockUIDFromSerialNumber (device.serialNumber);
const auto version = getVersionNumber (uid);
const auto name = getName (uid);
dc.device1 = getDeviceIDFromIndex (connection.device1);
dc.device2 = getDeviceIDFromIndex (connection.device2);
// For backwards compatibility we assume the first device we see in a group is the master and won't change
if (masterBlock == 0)
masterBlock = uid;
if (dc.device1 <= 0 || dc.device2 <= 0)
jassertfalse;
currentDeviceInfo.add ({ uid,
device.index,
device.serialNumber,
version,
name,
masterBlock == uid });
}
dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, connection.port1);
dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, connection.port2);
return dc;
}
void refreshCurrentDeviceConnections()
void updateCurrentDeviceConnections()
{
currentDeviceConnections.clearQuick();
currentDeviceConnections.swapWith (incomingTopologyConnections);
for (auto&& c : currentTopologyConnections)
{
BlockDeviceConnection dc;
dc.device1 = getDeviceIDFromIndex (c.device1);
dc.device2 = getDeviceIDFromIndex (c.device2);
if (dc.device1 <= 0 || dc.device2 <= 0)
continue;
dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, c.port1);
dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, c.port2);
currentDeviceConnections.add (dc);
}
detector.handleConnectionsChanged();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectedDeviceGroup)


+ 16
- 5
modules/juce_blocks_basics/topology/internal/juce_DepreciatedVersionReader.cpp View File

@@ -55,8 +55,8 @@ public:
for (size_t i = 1; i < numFirmwareApps; ++i)
{
const BlocksVersion highest { asString (highestVersion) };
const BlocksVersion test { asString ( result[i]) };
const BlocksVersion highest { highestVersion.asString() };
const BlocksVersion test { result[i].asString() };
if (highest < test)
highestVersion = result[i];
@@ -80,10 +80,21 @@ private:
{
static constexpr size_t requestSize { 8 };
static constexpr uint8 requests[][requestSize] = {{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x00, 0xf7 }, // Main App
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x01, 0xf7 }, // Stm32
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x03, 0xf7 }}; // Bootloader
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x03, 0xf7 }, // Bootloader
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x01, 0xf7 }}; // Stm32
deviceConnection.sendMessageToDevice (&requests[currentRequest.get()][0], requestSize);
static const BlocksVersion depreciatedVersion ("0.3.0");
if (currentRequest.get() == numFirmwareApps - 1
&& (BlocksVersion (result[0].asString()) >= depreciatedVersion
|| BlocksVersion (result[1].asString()) >= depreciatedVersion))
{
stopTimer();
}
else
{
deviceConnection.sendMessageToDevice (&requests[currentRequest.get()][0], requestSize);
}
}
//==============================================================================


+ 217
- 190
modules/juce_blocks_basics/topology/internal/juce_Detector.cpp View File

@@ -23,40 +23,11 @@
namespace juce
{
namespace
{
static bool containsBlockWithUID (const Block::Array& blocks, Block::UID uid) noexcept
{
for (auto&& block : blocks)
if (block->uid == uid)
return true;
return false;
}
static bool versionNumberChanged (const DeviceInfo& device, juce::String version) noexcept
{
auto deviceVersion = asString (device.version);
return deviceVersion != version && deviceVersion.isNotEmpty();
}
static void setVersionNumberForBlock (const DeviceInfo& deviceInfo, Block& block) noexcept
{
jassert (deviceInfo.uid == block.uid);
block.versionNumber = asString (deviceInfo.version);
}
static void setNameForBlock (const DeviceInfo& deviceInfo, Block& block)
{
jassert (deviceInfo.uid == block.uid);
block.name = asString (deviceInfo.name);
}
}
//==============================================================================
/** This is the main singleton object that keeps track of connected blocks */
struct Detector : public juce::ReferenceCountedObject,
private juce::Timer
private juce::Timer,
private juce::AsyncUpdater
{
using BlockImpl = BlockImplementation<Detector>;
@@ -100,11 +71,10 @@ struct Detector : public juce::ReferenceCountedObject,
if (activeTopologySources.isEmpty())
{
for (auto& b : currentTopology.blocks)
if (auto bi = BlockImpl::getFrom (*b))
if (auto bi = BlockImpl::getFrom (b))
bi->sendCommandMessage (BlocksProtocol::endAPIMode);
currentTopology = {};
lastTopology = {};
auto& d = getDefaultDetectorPointer();
@@ -124,76 +94,127 @@ struct Detector : public juce::ReferenceCountedObject,
return false;
}
const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
void handleDeviceAdded (const DeviceInfo& info)
{
for (auto d : connectedDeviceGroups)
if (auto status = d->getLastStatus (deviceID))
return status;
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
return nullptr;
}
const auto blockWasRemoved = containsBlockWithUID (blocksToRemove, info.uid);
const auto knownBlock = std::find_if (previouslySeenBlocks.begin(), previouslySeenBlocks.end(),
[uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
void handleTopologyChange()
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
Block::Ptr block;
if (knownBlock != previouslySeenBlocks.end())
{
juce::Array<DeviceInfo> newDeviceInfo;
juce::Array<BlockDeviceConnection> newDeviceConnections;
block = *knownBlock;
for (auto d : connectedDeviceGroups)
if (auto* blockImpl = BlockImpl::getFrom (*block))
{
newDeviceInfo.addArray (d->getCurrentDeviceInfo());
newDeviceConnections.addArray (d->getCurrentDeviceConnections());
blockImpl->markReconnected (info);
previouslySeenBlocks.removeObject (block);
}
}
else
{
block = new BlockImpl (*this, info);
}
for (int i = currentTopology.blocks.size(); --i >= 0;)
{
auto currentBlock = currentTopology.blocks.getUnchecked (i);
currentTopology.blocks.addIfNotAlreadyThere (block);
auto newDeviceIter = std::find_if (newDeviceInfo.begin(), newDeviceInfo.end(),
[&] (DeviceInfo& info) { return info.uid == currentBlock->uid; });
if (blockWasRemoved)
{
blocksToUpdate.addIfNotAlreadyThere (block);
blocksToAdd.removeObject (block);
}
else
{
blocksToAdd.addIfNotAlreadyThere (block);
blocksToUpdate.removeObject (block);
}
auto* blockImpl = BlockImpl::getFrom (*currentBlock);
blocksToRemove.removeObject (block);
if (newDeviceIter == newDeviceInfo.end())
{
if (blockImpl != nullptr)
blockImpl->markDisconnected();
triggerAsyncUpdate();
}
disconnectedBlocks.addIfNotAlreadyThere (currentTopology.blocks.removeAndReturn (i).get());
}
else
{
if (blockImpl != nullptr && blockImpl->wasPowerCycled())
{
blockImpl->resetPowerCycleFlag();
blockImpl->markReconnected (*newDeviceIter);
}
void handleDeviceRemoved (const DeviceInfo& info)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
updateCurrentBlockInfo (currentBlock, *newDeviceIter);
}
}
const auto blockIt = std::find_if (currentTopology.blocks.begin(), currentTopology.blocks.end(),
[uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
if (blockIt != currentTopology.blocks.end())
{
const auto block = *blockIt;
if (auto blockImpl = BlockImpl::getFrom (block))
blockImpl->markDisconnected();
currentTopology.blocks.removeObject (block);
previouslySeenBlocks.addIfNotAlreadyThere (block);
blocksToRemove.addIfNotAlreadyThere (block);
blocksToUpdate.removeObject (block);
blocksToAdd.removeObject (block);
triggerAsyncUpdate();
}
}
static const int maxBlocksToSave = 100;
void handleConnectionsChanged()
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
triggerAsyncUpdate();
}
void handleDeviceUpdated (const DeviceInfo& info)
{
if (containsBlockWithUID (blocksToRemove, info.uid))
return;
if (disconnectedBlocks.size() > maxBlocksToSave)
disconnectedBlocks.removeRange (0, 2 * (disconnectedBlocks.size() - maxBlocksToSave));
const auto blockIt = std::find_if (currentTopology.blocks.begin(), currentTopology.blocks.end(),
[uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
for (auto& info : newDeviceInfo)
if (info.serial.isValid() && ! containsBlockWithUID (currentTopology.blocks, getBlockUIDFromSerialNumber (info.serial)))
addBlock (info);
if (blockIt != currentTopology.blocks.end())
{
const auto block = *blockIt;
if (auto blockImpl = BlockImpl::getFrom (block))
blockImpl->markReconnected (info);
currentTopology.connections.swapWith (newDeviceConnections);
if (! containsBlockWithUID (blocksToAdd, info.uid))
{
blocksToUpdate.addIfNotAlreadyThere (block);
triggerAsyncUpdate();
}
}
}
void handleBatteryChargingChanged (Block::UID deviceID, const BlocksProtocol::BatteryCharging isCharging)
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
if (auto blockImpl = BlockImpl::getFrom (*block))
blockImpl->batteryCharging = isCharging;
}
void handleBatteryLevelChanged (Block::UID deviceID, const BlocksProtocol::BatteryLevel batteryLevel)
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
if (auto blockImpl = BlockImpl::getFrom (*block))
blockImpl->batteryLevel = batteryLevel;
}
broadcastTopology();
void handleIndexChanged (Block::UID deviceID, const BlocksProtocol::TopologyIndex index)
{
if (auto block = currentTopology.getBlockWithUID (deviceID))
if (auto blockImpl = BlockImpl::getFrom (*block))
blockImpl->topologyIndex = index;
}
void notifyBlockIsRestarting (Block::UID deviceID)
{
for (auto& group : connectedDeviceGroups)
group->notifyBlockIsRestarting (deviceID);
group->handleBlockRestarting (deviceID);
}
void handleSharedDataACK (Block::UID deviceID, uint32 packetCounter) const
@@ -305,24 +326,11 @@ struct Detector : public juce::ReferenceCountedObject,
}
//==============================================================================
int getIndexFromDeviceID (Block::UID deviceID) const noexcept
{
for (auto* c : connectedDeviceGroups)
{
auto index = c->getIndexFromDeviceID (deviceID);
if (index >= 0)
return index;
}
return -1;
}
template <typename PacketBuilder>
bool sendMessageToDevice (Block::UID deviceID, const PacketBuilder& builder) const
{
for (auto* c : connectedDeviceGroups)
if (c->getIndexFromDeviceID (deviceID) >= 0)
if (c->contains (deviceID))
return c->sendMessageToDevice (builder);
return false;
@@ -341,11 +349,8 @@ struct Detector : public juce::ReferenceCountedObject,
{
for (const auto& d : connectedDeviceGroups)
{
for (const auto& info : d->getCurrentDeviceInfo())
{
if (info.uid == b.uid)
return d->getDeviceConnection();
}
if (d->contains (b.uid))
return d->getDeviceConnection();
}
return nullptr;
@@ -355,11 +360,8 @@ struct Detector : public juce::ReferenceCountedObject,
{
for (const auto& d : connectedDeviceGroups)
{
for (const auto& info : d->getCurrentDeviceInfo())
{
if (info.uid == b.uid)
return d->getDeviceConnection();
}
if (d->contains (b.uid))
return d->getDeviceConnection();
}
return nullptr;
@@ -370,10 +372,11 @@ struct Detector : public juce::ReferenceCountedObject,
juce::Array<PhysicalTopologySource*> activeTopologySources;
BlockTopology currentTopology, lastTopology;
juce::ReferenceCountedArray<Block, CriticalSection> disconnectedBlocks;
BlockTopology currentTopology;
private:
Block::Array previouslySeenBlocks, blocksToAdd, blocksToRemove, blocksToUpdate;
void timerCallback() override
{
startTimer (1500);
@@ -384,21 +387,20 @@ private:
handleDevicesAdded (detectedDevices);
}
void handleDevicesRemoved (const juce::StringArray& detectedDevices)
bool containsBlockWithUID (const juce::Block::Array& blocks, juce::Block::UID uid)
{
bool anyDevicesRemoved = false;
for (const auto block : blocks)
if (block->uid == uid)
return true;
return false;
}
void handleDevicesRemoved (const juce::StringArray& detectedDevices)
{
for (int i = connectedDeviceGroups.size(); --i >= 0;)
{
if (! connectedDeviceGroups.getUnchecked(i)->isStillConnected (detectedDevices))
{
connectedDeviceGroups.remove (i);
anyDevicesRemoved = true;
}
}
if (anyDevicesRemoved)
handleTopologyChange();
}
void handleDevicesAdded (const juce::StringArray& detectedDevices)
@@ -424,57 +426,9 @@ private:
return false;
}
void addBlock (DeviceInfo info)
{
if (! reactivateBlockIfKnown (info))
addNewBlock (info);
}
bool reactivateBlockIfKnown (DeviceInfo info)
{
const auto uid = getBlockUIDFromSerialNumber (info.serial);
for (int i = disconnectedBlocks.size(); --i >= 0;)
{
if (uid != disconnectedBlocks.getUnchecked (i)->uid)
continue;
auto block = disconnectedBlocks.removeAndReturn (i);
if (auto* blockImpl = BlockImpl::getFrom (*block))
{
blockImpl->markReconnected (info);
currentTopology.blocks.add (block);
return true;
}
}
return false;
}
void addNewBlock (DeviceInfo info)
{
currentTopology.blocks.add (new BlockImpl (info.serial, *this, info.version,
info.name, info.isMaster));
}
void updateCurrentBlockInfo (Block::Ptr blockToUpdate, DeviceInfo& updatedInfo)
{
jassert (updatedInfo.uid == blockToUpdate->uid);
if (versionNumberChanged (updatedInfo, blockToUpdate->versionNumber))
setVersionNumberForBlock (updatedInfo, *blockToUpdate);
if (updatedInfo.name.isNotEmpty())
setNameForBlock (updatedInfo, *blockToUpdate);
if (updatedInfo.isMaster != blockToUpdate->isMasterBlock())
BlockImpl::getFrom (*blockToUpdate)->setToMaster (updatedInfo.isMaster);
}
BlockImpl* getBlockImplementationWithUID (Block::UID deviceID) const noexcept
{
if (auto&& block = currentTopology.getBlockWithUID (deviceID))
if (auto block = currentTopology.getBlockWithUID (deviceID))
return BlockImpl::getFrom (*block);
return nullptr;
@@ -484,31 +438,41 @@ private:
//==============================================================================
/** This is a friend of the BlocksImplementation that will scan and set the
physical positions of the blocks */
struct BlocksTraverser
physical positions of the blocks.
Returns an array of blocks that were updated.
*/
struct BlocksLayoutTraverser
{
void traverseBlockArray (const BlockTopology& topology)
static Block::Array updateBlocks (const BlockTopology& topology)
{
Block::Array updated;
juce::Array<Block::UID> visited;
for (auto& block : topology.blocks)
{
if (block->isMasterBlock() && ! visited.contains (block->uid))
{
if (auto* bi = dynamic_cast<BlockImpl*> (block))
if (auto* bi = BlockImpl::getFrom (block))
{
bi->masterUID = {};
bi->position = {};
bi->rotation = 0;
if (bi->rotation != 0 || bi->position.first != 0 || bi->position.second != 0)
{
bi->rotation = 0;
bi->position = {};
updated.add (block);
}
}
layoutNeighbours (*block, topology, block->uid, visited);
layoutNeighbours (*block, topology, visited, updated);
}
}
return updated;
}
private:
// returns the distance from corner clockwise
int getUnitForIndex (Block::Ptr block, Block::ConnectionPort::DeviceEdge edge, int index)
static int getUnitForIndex (Block::Ptr block, Block::ConnectionPort::DeviceEdge edge, int index)
{
if (block->getType() == Block::seaboardBlock)
{
@@ -533,7 +497,7 @@ private:
}
// returns how often north needs to rotate by 90 degrees
int getRotationForEdge (Block::ConnectionPort::DeviceEdge edge)
static int getRotationForEdge (Block::ConnectionPort::DeviceEdge edge)
{
switch (edge)
{
@@ -547,8 +511,10 @@ private:
return 0;
}
void layoutNeighbours (Block::Ptr block, const BlockTopology& topology,
Block::UID masterUid, juce::Array<Block::UID>& visited)
static void layoutNeighbours (const Block::Ptr block,
const BlockTopology& topology,
juce::Array<Block::UID>& visited,
Block::Array& updated)
{
visited.add (block->uid);
@@ -568,10 +534,17 @@ private:
const auto myOffset = getUnitForIndex (block, myPort.edge, myPort.index);
const auto theirOffset = getUnitForIndex (neighbourPtr, theirPort.edge, theirPort.index);
neighbour->masterUID = masterUid;
neighbour->rotation = (2 + block->getRotation()
+ getRotationForEdge (myPort.edge)
- getRotationForEdge (theirPort.edge)) % 4;
{
const auto neighbourRotation = (2 + block->getRotation()
+ getRotationForEdge (myPort.edge)
- getRotationForEdge (theirPort.edge)) % 4;
if (neighbour->rotation != neighbourRotation)
{
neighbour->rotation = neighbourRotation;
updated.addIfNotAlreadyThere (neighbourPtr);
}
}
std::pair<int, int> delta;
const auto theirBounds = neighbour->getBlockAreaWithinLayout();
@@ -592,10 +565,22 @@ private:
break;
}
neighbour->position = { myBounds.x + delta.first, myBounds.y + delta.second };
}
{
const auto neighbourX = myBounds.x + delta.first;
const auto neighbourY = myBounds.y + delta.second;
layoutNeighbours (neighbourPtr, topology, masterUid, visited);
if (neighbour->position.first != neighbourX
|| neighbour->position.second != neighbourY)
{
neighbour->position.first = neighbourX;
neighbour->position.second = neighbourY;
updated.addIfNotAlreadyThere (neighbourPtr);
}
}
layoutNeighbours (neighbourPtr, topology, visited, updated);
}
}
}
}
@@ -674,22 +659,64 @@ private:
#endif
//==============================================================================
void broadcastTopology()
void updateBlockPositions()
{
if (currentTopology != lastTopology)
const auto updated = BlocksLayoutTraverser::updateBlocks (currentTopology);
for (const auto block : updated)
{
lastTopology = currentTopology;
if (containsBlockWithUID (blocksToAdd, block->uid) || containsBlockWithUID (blocksToRemove, block->uid))
continue;
BlocksTraverser traverser;
traverser.traverseBlockArray (currentTopology);
blocksToUpdate.addIfNotAlreadyThere (block);
}
}
for (auto* d : activeTopologySources)
d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
void updateBlockConnections()
{
currentTopology.connections.clearQuick();
for (auto d : connectedDeviceGroups)
currentTopology.connections.addArray (d->getCurrentDeviceConnections());
}
void handleAsyncUpdate() override
{
updateBlockConnections();
updateBlockPositions();
for (auto* d : activeTopologySources)
{
for (const auto block : blocksToAdd)
d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockAdded (block); });
for (const auto block : blocksToRemove)
d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockRemoved (block); });
for (const auto block : blocksToUpdate)
d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockUpdated (block); });
}
const auto topologyChanged = blocksToAdd.size() > 0 || blocksToRemove.size() > 0 || blocksToUpdate.size() > 0;
if (topologyChanged)
{
#if DUMP_TOPOLOGY
dumpTopology (lastTopology);
dumpTopology (currentTopology);
#endif
for (auto* d : activeTopologySources)
d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
}
blocksToUpdate.clear();
blocksToAdd.clear();
blocksToRemove.clear();
static const int maxBlocksToSave = 100;
if (previouslySeenBlocks.size() > maxBlocksToSave)
previouslySeenBlocks.removeRange (0, 2 * (previouslySeenBlocks.size() - maxBlocksToSave));
}
//==============================================================================


+ 10
- 3
modules/juce_blocks_basics/topology/internal/juce_DeviceInfo.cpp View File

@@ -26,9 +26,13 @@ namespace juce
struct DeviceInfo
{
// VS2015 requires a constructor to avoid aggregate initialization
DeviceInfo (Block::UID buid, BlocksProtocol::TopologyIndex tidx, BlocksProtocol::BlockSerialNumber s,
BlocksProtocol::VersionNumber v, BlocksProtocol::BlockName n, bool master = false)
: uid (buid), index (tidx), serial (s), version (v), name (n), isMaster (master)
DeviceInfo (Block::UID buid, BlocksProtocol::TopologyIndex tidx,
BlocksProtocol::BlockSerialNumber s, BlocksProtocol::VersionNumber v,
BlocksProtocol::BlockName n, BlocksProtocol::BatteryLevel level,
BlocksProtocol::BatteryCharging charging, Block::UID master)
: uid (buid), index (tidx), serial (s), version (v), name (n),
batteryLevel (level), batteryCharging (charging), masterUid (master),
isMaster (uid == master)
{
}
@@ -37,6 +41,9 @@ struct DeviceInfo
BlocksProtocol::BlockSerialNumber serial;
BlocksProtocol::VersionNumber version;
BlocksProtocol::BlockName name;
BlocksProtocol::BatteryLevel batteryLevel;
BlocksProtocol::BatteryCharging batteryCharging;
Block::UID masterUid;
bool isMaster {};
};


+ 0
- 11
modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp View File

@@ -47,17 +47,6 @@
#include "internal/juce_BandwidthStatsLogger.cpp"
#endif
namespace
{
/** Helper function to create juce::String from BlockStringData */
template <size_t MaxSize>
juce::String asString (juce::BlocksProtocol::BlockStringData<MaxSize> blockString)
{
return { reinterpret_cast<const char*> (blockString.data),
juce::jmin (sizeof (blockString.data), static_cast<size_t> (blockString.length))};
}
}
#include "internal/juce_MidiDeviceConnection.cpp"
#include "internal/juce_MIDIDeviceDetector.cpp"
#include "internal/juce_DeviceInfo.cpp"


+ 14
- 1
modules/juce_blocks_basics/topology/juce_TopologySource.h View File

@@ -48,7 +48,20 @@ public:
struct Listener
{
virtual ~Listener() = default;
virtual void topologyChanged() = 0;
/** Called for any change in topology - devices changed, connections changed, etc. */
virtual void topologyChanged() {}
/** Called when a new block is added to the topology. */
virtual void blockAdded (const Block::Ptr) {}
/** Called when a block is removed from the topology. */
virtual void blockRemoved (const Block::Ptr) {}
/** Called when a known block is updated.
This could be becasue details have been reveived asyncroniously. E.g. Block name.
*/
virtual void blockUpdated (const Block::Ptr) {}
};
void addListener (Listener* l) { listeners.add (l); }


Loading…
Cancel
Save