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:
parent
da4c1eaf31
commit
7cf52297ee
11 changed files with 726 additions and 664 deletions
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue