1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-22 01:34:21 +00:00

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

This commit is contained in:
ed 2019-02-18 15:03:42 +00:00
parent da4c1eaf31
commit 7cf52297ee
11 changed files with 726 additions and 664 deletions

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());

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.

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,

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>();

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;

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();
}
~ConnectedDeviceGroup()
{
for (const auto& device : currentDeviceInfo)
detector.handleDeviceRemoved (device);
}
bool isStillConnected (const juce::StringArray& detectedDevices) const noexcept
{
return detectedDevices.contains (deviceName) && ! failedToGetTopology();
}
int getIndexFromDeviceID (Block::UID uid) const noexcept
bool contains (Block::UID uid)
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return d.index;
return -1;
return getIndexFromDeviceID (uid) >= 0;
}
const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
{
for (auto&& status : currentTopologyDevices)
if (getBlockUIDFromSerialNumber (status.serialNumber) == deviceID)
return &status;
return nullptr;
}
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);
LOG_CONNECTIVITY ("Valid topology received");
incomingTopologyDevices.clearQuick();
incomingTopologyConnections.clearQuick();
refreshCurrentDeviceInfo();
refreshCurrentDeviceConnections();
removeUnusedBlocksFromMap (versionNumbers, currentDeviceInfo);
removeUnusedBlocksFromMap (blockNames, currentDeviceInfo);
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; };
@ -379,32 +303,6 @@ private:
Block::UID masterBlock = 0;
//==============================================================================
void setMidiMessageCallback()
{
deviceConnection->handleMessageFromDevice = [this] (const void* data, size_t dataSize)
{
this->handleIncomingMessage (data, dataSize);
};
}
//==============================================================================
juce::Time lastTopologyRequestTime, lastTopologyReceiveTime;
int numTopologyRequestsSent = 0;
void scheduleNewTopologyRequest()
{
numTopologyRequestsSent = 0;
lastTopologyReceiveTime = juce::Time();
lastTopologyRequestTime = juce::Time::getCurrentTime();
}
void sendTopologyRequest()
{
++numTopologyRequestsSent;
lastTopologyRequestTime = juce::Time::getCurrentTime();
sendCommandMessage (0, BlocksProtocol::requestTopologyMessage);
}
void timerCallback() override
{
const auto now = juce::Time::getCurrentTime();
@ -419,191 +317,15 @@ private:
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()
void setMidiMessageCallback()
{
if (depreciatedVersionReader == nullptr)
return;
const auto masterVersion = depreciatedVersionReader->getVersionNumber();
if (masterVersion.isNotEmpty())
setVersion (masterBlock, masterVersion);
}
void setVersion (const Block::UID uid, const BlocksProtocol::VersionNumber versionNumber)
{
if (uid == masterBlock)
depreciatedVersionReader.reset();
if (insertOrAssign (versionNumbers, uid, versionNumber))
deviceConnection->handleMessageFromDevice = [this] (const void* data, size_t dataSize)
{
refreshCurrentDeviceInfo();
detector.handleTopologyChange();
}
}
//==============================================================================
struct BlockPingTime
{
Block::UID blockUID;
juce::Time lastPing;
juce::Time connected;
};
juce::Array<BlockPingTime> blockPings;
void updateApiPing (Block::UID uid)
{
const auto now = juce::Time::getCurrentTime();
if (auto* ping = getPing (uid))
{
LOG_PING ("Ping: " << uid << " " << now.formatted ("%Mm %Ss"));
ping->lastPing = now;
}
else
{
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; });
}
bool isApiConnected (Block::UID uid)
{
return getPing (uid) != nullptr;
}
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();
}
}
void checkApiTimeouts (juce::Time now)
{
const auto timedOut = [this, now] (BlockPingTime& ping)
{
if (ping.lastPing >= now - juce::RelativeTime::seconds (pingTimeoutSeconds))
return false;
LOG_CONNECTIVITY ("Ping timeout: " << ping.blockUID);
removeDeviceInfo (ping.blockUID);
return true;
this->handleIncomingMessage (data, dataSize);
};
if (blockPings.removeIf (timedOut) > 0)
{
scheduleNewTopologyRequest();
detector.handleTopologyChange();
}
}
/* Returns true is ping was removed */
void removePingForDisconnectedBlocks()
{
const auto removed = [this] (auto& ping)
{
for (auto& info : currentDeviceInfo)
if (info.uid == ping.blockUID)
return false;
LOG_CONNECTIVITY ("API Disconnected by topology update " << ping.blockUID);
return true;
};
blockPings.removeIf (removed);
}
void startApiModeOnConnectedBlocks()
{
for (auto& info : currentDeviceInfo)
{
if (! isApiConnected (info.uid))
{
LOG_CONNECTIVITY ("API Try " << info.uid);
sendCommandMessage (info.index, BlocksProtocol::endAPIMode);
sendCommandMessage (info.index, BlocksProtocol::beginAPIMode);
}
}
}
//==============================================================================
void checkVersionNumberTimeouts()
{
for (const auto& device : currentDeviceInfo)
{
const auto version = versionNumbers.find (device.uid);
if (version == versionNumbers.end())
{
auto* ping = getPing (device.uid);
}
}
}
//==============================================================================
Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.index == index)
return d.uid;
return {};
}
Block::UID getDeviceIDFromMessageIndex (BlocksProtocol::TopologyIndex index) noexcept
{
const auto uid = getDeviceIDFromIndex (index);
// re-request topology if we get an event from an unknown block
if (uid == Block::UID())
scheduleNewTopologyRequest();
return uid;
}
//==============================================================================
void handleIncomingMessage (const void* data, size_t dataSize)
{
juce::MemoryBlock mb (data, dataSize);
@ -639,21 +361,200 @@ private:
}
}
//==============================================================================
BlocksProtocol::VersionNumber getVersionNumber (Block::UID uid)
bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
{
const auto version = versionNumbers.find (uid);
return version == versionNumbers.end() ? BlocksProtocol::VersionNumber() : version->second;
}
BlocksProtocol::BlockName getName (Block::UID uid)
{
const auto name = blockNames.find (uid);
return name == blockNames.end() ? BlocksProtocol::BlockName() : name->second;
BlocksProtocol::HostPacketBuilder<64> p;
p.writePacketSysexHeaderBytes (deviceIndex);
p.deviceControlMessage (commandID);
p.writePacketSysexFooter();
return sendMessageToDevice (p);
}
//==============================================================================
const DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
juce::Time lastTopologyRequestTime, lastTopologyReceiveTime;
int numTopologyRequestsSent = 0;
void scheduleNewTopologyRequest()
{
LOG_CONNECTIVITY ("Topology Request Scheduled");
numTopologyRequestsSent = 0;
lastTopologyReceiveTime = juce::Time();
lastTopologyRequestTime = juce::Time::getCurrentTime();
}
void sendTopologyRequest()
{
++numTopologyRequestsSent;
lastTopologyRequestTime = juce::Time::getCurrentTime();
sendCommandMessage (0, BlocksProtocol::requestTopologyMessage);
}
bool failedToGetTopology() const noexcept
{
return numTopologyRequestsSent >= 4 && lastTopologyReceiveTime == juce::Time();
}
//==============================================================================
void requestMasterBlockVersionIfNeeded()
{
if (depreciatedVersionReader == nullptr)
return;
const auto masterVersion = depreciatedVersionReader->getVersionNumber();
if (masterVersion.isNotEmpty())
{
const auto masterIndex = getIndexFromDeviceID (masterBlock);
if (masterIndex >= 0)
setVersion (BlocksProtocol::TopologyIndex (masterIndex), masterVersion);
else
jassertfalse;
}
}
void setVersion (const BlocksProtocol::TopologyIndex index, const BlocksProtocol::VersionNumber versionNumber)
{
if (versionNumber.length <= 1)
return;
if (const auto info = getDeviceInfoFromIndex (index))
{
if (info->version == versionNumber)
return;
if (info->uid == masterBlock)
depreciatedVersionReader.reset();
info->version = versionNumber;
detector.handleDeviceUpdated (*info);
}
}
//==============================================================================
struct BlockPingTime
{
Block::UID blockUID;
juce::Time lastPing;
juce::Time connected;
};
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();
if (auto* ping = getPing (uid))
{
LOG_PING ("Ping: " << uid << " " << now.formatted ("%Mm %Ss"));
ping->lastPing = now;
}
else
{
LOG_CONNECTIVITY ("API Connected " << uid);
blockPings.add ({ uid, now, now });
if (const auto info = getDeviceInfoFromUID (uid))
detector.handleDeviceAdded (*info);
}
}
bool isApiConnected (Block::UID uid)
{
return getPing (uid) != nullptr;
}
void forceApiDisconnected (Block::UID /*uid*/)
{
Array<Block::UID> toRemove;
for (const auto& ping : blockPings)
toRemove.add (ping.blockUID);
for (const auto& uid : toRemove)
removeDevice (uid);
scheduleNewTopologyRequest();
}
void checkApiTimeouts (juce::Time now)
{
Array<Block::UID> toRemove;
for (const auto& ping : blockPings)
{
if (ping.lastPing < now - juce::RelativeTime::seconds (pingTimeoutSeconds))
{
LOG_CONNECTIVITY ("Ping timeout: " << ping.blockUID);
toRemove.add (ping.blockUID);
scheduleNewTopologyRequest();
}
}
for (const auto& uid : toRemove)
removeDevice (uid);
}
void startApiModeOnConnectedBlocks()
{
for (auto& info : currentDeviceInfo)
{
if (! isApiConnected (info.uid))
{
LOG_CONNECTIVITY ("API Try " << info.uid);
sendCommandMessage (info.index, BlocksProtocol::endAPIMode);
sendCommandMessage (info.index, BlocksProtocol::beginAPIMode);
}
}
}
//==============================================================================
Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) noexcept
{
for (const auto& device : currentDeviceInfo)
if (device.index == index)
return device.uid;
scheduleNewTopologyRequest();
return {};
}
int getIndexFromDeviceID (Block::UID uid) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
return d.index;
return -1;
}
DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.uid == uid)
@ -662,57 +563,132 @@ private:
return nullptr;
}
DeviceInfo* getDeviceInfoFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
{
for (auto& d : currentDeviceInfo)
if (d.index == index)
return &d;
return nullptr;
}
void removeDeviceInfo (Block::UID uid)
{
currentDeviceInfo.removeIf ([uid] (const DeviceInfo& info) { return info.uid == uid; });
}
const DeviceStatus* getIncomingDeviceStatus (BlockSerialNumber serialNumber) const
{
for (auto& device : incomingTopologyDevices)
if (device.serialNumber == serialNumber)
return &device;
return nullptr;
}
//==============================================================================
void removeDevice (Block::UID uid)
{
if (const auto info = getDeviceInfoFromUID (uid))
detector.handleDeviceRemoved (*info);
removeDeviceInfo (uid);
removePing (uid);
}
void updateCurrentDeviceList()
{
Array<Block::UID> toRemove;
//Update known devices
for (int i = currentDeviceInfo.size(); --i >= 0; )
{
auto& currentDevice = currentDeviceInfo.getReference (i);
if (const auto newStatus = getIncomingDeviceStatus (currentDevice.serial))
{
if (currentDevice.index != newStatus->index)
{
currentDevice.index = newStatus->index;
detector.handleIndexChanged (currentDevice.uid, currentDevice.index);
}
if (currentDevice.batteryCharging != newStatus->batteryCharging)
{
currentDevice.batteryCharging = newStatus->batteryCharging;
detector.handleBatteryChargingChanged (currentDevice.uid, currentDevice.batteryCharging);
}
if (currentDevice.batteryLevel != newStatus->batteryLevel)
{
currentDevice.batteryLevel = newStatus->batteryLevel;
detector.handleBatteryLevelChanged (currentDevice.uid, currentDevice.batteryLevel);
}
}
else
{
toRemove.add (currentDevice.uid);
}
}
for (const auto& uid : toRemove)
removeDevice (uid);
//Add new devices
for (const auto& device : incomingTopologyDevices)
{
const auto uid = getBlockUIDFromSerialNumber (device.serialNumber);
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)

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);
}
}
//==============================================================================

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
{
for (auto d : connectedDeviceGroups)
if (auto status = d->getLastStatus (deviceID))
return status;
return nullptr;
}
void handleTopologyChange()
void handleDeviceAdded (const DeviceInfo& info)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
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; });
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);
}
for (int i = currentTopology.blocks.size(); --i >= 0;)
{
auto currentBlock = currentTopology.blocks.getUnchecked (i);
auto newDeviceIter = std::find_if (newDeviceInfo.begin(), newDeviceInfo.end(),
[&] (DeviceInfo& info) { return info.uid == currentBlock->uid; });
auto* blockImpl = BlockImpl::getFrom (*currentBlock);
if (newDeviceIter == newDeviceInfo.end())
{
if (blockImpl != nullptr)
blockImpl->markDisconnected();
disconnectedBlocks.addIfNotAlreadyThere (currentTopology.blocks.removeAndReturn (i).get());
}
else
{
if (blockImpl != nullptr && blockImpl->wasPowerCycled())
{
blockImpl->resetPowerCycleFlag();
blockImpl->markReconnected (*newDeviceIter);
}
updateCurrentBlockInfo (currentBlock, *newDeviceIter);
}
}
static const int maxBlocksToSave = 100;
if (disconnectedBlocks.size() > maxBlocksToSave)
disconnectedBlocks.removeRange (0, 2 * (disconnectedBlocks.size() - maxBlocksToSave));
for (auto& info : newDeviceInfo)
if (info.serial.isValid() && ! containsBlockWithUID (currentTopology.blocks, getBlockUIDFromSerialNumber (info.serial)))
addBlock (info);
currentTopology.connections.swapWith (newDeviceConnections);
}
else
{
block = new BlockImpl (*this, info);
}
broadcastTopology();
currentTopology.blocks.addIfNotAlreadyThere (block);
if (blockWasRemoved)
{
blocksToUpdate.addIfNotAlreadyThere (block);
blocksToAdd.removeObject (block);
}
else
{
blocksToAdd.addIfNotAlreadyThere (block);
blocksToUpdate.removeObject (block);
}
blocksToRemove.removeObject (block);
triggerAsyncUpdate();
}
void handleDeviceRemoved (const DeviceInfo& info)
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
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();
}
}
void handleConnectionsChanged()
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
triggerAsyncUpdate();
}
void handleDeviceUpdated (const DeviceInfo& info)
{
if (containsBlockWithUID (blocksToRemove, info.uid))
return;
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->markReconnected (info);
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;
}
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);
}
bool containsBlockWithUID (const juce::Block::Array& blocks, juce::Block::UID uid)
{
for (const auto block : blocks)
if (block->uid == uid)
return true;
return false;
}
void handleDevicesRemoved (const juce::StringArray& detectedDevices)
{
bool anyDevicesRemoved = false;
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)
{
lastTopology = currentTopology;
const auto updated = BlocksLayoutTraverser::updateBlocks (currentTopology);
BlocksTraverser traverser;
traverser.traverseBlockArray (currentTopology);
for (const auto block : updated)
{
if (containsBlockWithUID (blocksToAdd, block->uid) || containsBlockWithUID (blocksToRemove, block->uid))
continue;
blocksToUpdate.addIfNotAlreadyThere (block);
}
}
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 (currentTopology);
#endif
for (auto* d : activeTopologySources)
d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
#if DUMP_TOPOLOGY
dumpTopology (lastTopology);
#endif
}
blocksToUpdate.clear();
blocksToAdd.clear();
blocksToRemove.clear();
static const int maxBlocksToSave = 100;
if (previouslySeenBlocks.size() > maxBlocksToSave)
previouslySeenBlocks.removeRange (0, 2 * (previouslySeenBlocks.size() - maxBlocksToSave));
}
//==============================================================================

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 {};
};

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"

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); }