782 lines
22 KiB
C++
782 lines
22 KiB
C++
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Universe.cpp
|
|
* Represents a universe of DMX data.
|
|
* Copyright (C) 2005-2009 Simon Newton
|
|
*
|
|
* Each universe has the following:
|
|
* A human readable name
|
|
* A DmxBuffer with the current dmx data
|
|
* A MergeMode, either LTP (latest takes precedence) or HTP (highest takes
|
|
* precedence)
|
|
* A list of ports bound to this universe. If a port is an input, we use the
|
|
* data to update the DmxBuffer according to the MergeMode. If a port is an
|
|
* output, we notify the port whenever the DmxBuffer changes.
|
|
* A list of source clients. which provide us with data for updating the
|
|
* DmxBuffer per the merge mode.
|
|
* A list of sink clients, which we update whenever the DmxBuffer changes.
|
|
*/
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iterator>
|
|
#include <algorithm>
|
|
|
|
#include "ola/Logging.h"
|
|
#include "olad/Client.h"
|
|
#include "olad/UniverseStore.h"
|
|
#include "olad/Port.h"
|
|
#include "olad/Universe.h"
|
|
|
|
namespace ola {
|
|
|
|
const char Universe::K_UNIVERSE_UID_COUNT_VAR[] = "universe-uids";
|
|
const char Universe::K_FPS_VAR[] = "universe-dmx-frames";
|
|
const char Universe::K_MERGE_HTP_STR[] = "htp";
|
|
const char Universe::K_MERGE_LTP_STR[] = "ltp";
|
|
const char Universe::K_UNIVERSE_INPUT_PORT_VAR[] = "universe-input-ports";
|
|
const char Universe::K_UNIVERSE_MODE_VAR[] = "universe-mode";
|
|
const char Universe::K_UNIVERSE_NAME_VAR[] = "universe-name";
|
|
const char Universe::K_UNIVERSE_OUTPUT_PORT_VAR[] = "universe-output-ports";
|
|
const char Universe::K_UNIVERSE_RDM_REQUESTS[] = "universe-rdm-requests";
|
|
const char Universe::K_UNIVERSE_RDM_RESPONSES[] = "universe-rdm-responses";
|
|
const char Universe::K_UNIVERSE_SINK_CLIENTS_VAR[] = "universe-sink-clients";
|
|
const char Universe::K_UNIVERSE_SOURCE_CLIENTS_VAR[] =
|
|
"universe-source-clients";
|
|
|
|
/*
|
|
* Create a new universe
|
|
* @param uid the universe id of this universe
|
|
* @param store the store this universe came from
|
|
* @param export_map the ExportMap that we update
|
|
*/
|
|
Universe::Universe(unsigned int universe_id, UniverseStore *store,
|
|
ExportMap *export_map)
|
|
: m_universe_name(""),
|
|
m_universe_id(universe_id),
|
|
m_active_priority(DmxSource::PRIORITY_MIN),
|
|
m_merge_mode(Universe::MERGE_LTP),
|
|
m_universe_store(store),
|
|
m_export_map(export_map) {
|
|
stringstream universe_id_str, universe_name_str;
|
|
universe_id_str << universe_id;
|
|
m_universe_id_str = universe_id_str.str();
|
|
universe_name_str << "Universe " << universe_id;
|
|
m_universe_name = universe_name_str.str();
|
|
|
|
UpdateName();
|
|
UpdateMode();
|
|
|
|
const char *vars[] = {
|
|
K_FPS_VAR,
|
|
K_UNIVERSE_INPUT_PORT_VAR,
|
|
K_UNIVERSE_OUTPUT_PORT_VAR,
|
|
K_UNIVERSE_RDM_REQUESTS,
|
|
K_UNIVERSE_RDM_RESPONSES,
|
|
K_UNIVERSE_SINK_CLIENTS_VAR,
|
|
K_UNIVERSE_SOURCE_CLIENTS_VAR,
|
|
K_UNIVERSE_UID_COUNT_VAR,
|
|
};
|
|
|
|
if (m_export_map) {
|
|
for (unsigned int i = 0; i < sizeof(vars) / sizeof(vars[0]); ++i)
|
|
(*m_export_map->GetUIntMapVar(vars[i]))[m_universe_id_str] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Delete this universe
|
|
*/
|
|
Universe::~Universe() {
|
|
const char *string_vars[] = {
|
|
K_UNIVERSE_NAME_VAR,
|
|
K_UNIVERSE_MODE_VAR,
|
|
};
|
|
|
|
const char *uint_vars[] = {
|
|
K_FPS_VAR,
|
|
K_UNIVERSE_INPUT_PORT_VAR,
|
|
K_UNIVERSE_OUTPUT_PORT_VAR,
|
|
K_UNIVERSE_RDM_REQUESTS,
|
|
K_UNIVERSE_RDM_RESPONSES,
|
|
K_UNIVERSE_SINK_CLIENTS_VAR,
|
|
K_UNIVERSE_SOURCE_CLIENTS_VAR,
|
|
K_UNIVERSE_UID_COUNT_VAR,
|
|
};
|
|
|
|
if (m_export_map) {
|
|
for (unsigned int i = 0; i < sizeof(string_vars) / sizeof(char*); ++i)
|
|
m_export_map->GetStringMapVar(string_vars[i])->Remove(m_universe_id_str);
|
|
for (unsigned int i = 0; i < sizeof(uint_vars) / sizeof(char*); ++i)
|
|
m_export_map->GetUIntMapVar(uint_vars[i])->Remove(m_universe_id_str);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the universe name
|
|
* @param name the new universe name
|
|
*/
|
|
void Universe::SetName(const string &name) {
|
|
m_universe_name = name;
|
|
UpdateName();
|
|
|
|
// notify ports
|
|
vector<OutputPort*>::const_iterator iter;
|
|
for (iter = m_output_ports.begin(); iter != m_output_ports.end(); ++iter) {
|
|
(*iter)->UniverseNameChanged(name);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the universe merge mode
|
|
* @param merge_mode the new merge_mode
|
|
*/
|
|
void Universe::SetMergeMode(enum merge_mode merge_mode) {
|
|
m_merge_mode = merge_mode;
|
|
UpdateMode();
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an InputPort to this universe.
|
|
* @param port the port to add
|
|
*/
|
|
bool Universe::AddPort(InputPort *port) {
|
|
return GenericAddPort(port, &m_input_ports);
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an OutputPort to this universe.
|
|
* @param port the port to add
|
|
*/
|
|
bool Universe::AddPort(OutputPort *port) {
|
|
return GenericAddPort(port, &m_output_ports);
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove a port from this universe.
|
|
* @param port the port to remove
|
|
* @return true if the port was removed, false if it didn't exist
|
|
*/
|
|
bool Universe::RemovePort(InputPort *port) {
|
|
return GenericRemovePort(port, &m_input_ports, &m_input_uids);
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove a port from this universe.
|
|
* @param port the port to remove
|
|
* @return true if the port was removed, false if it didn't exist
|
|
*/
|
|
bool Universe::RemovePort(OutputPort *port) {
|
|
bool ret = GenericRemovePort(port, &m_output_ports, &m_output_uids);
|
|
|
|
if (m_export_map)
|
|
(*m_export_map->GetUIntMapVar(K_UNIVERSE_UID_COUNT_VAR))[m_universe_id_str]
|
|
= m_output_uids.size();
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if this port is bound to this universe
|
|
* @param port the port to check for
|
|
* @return true if the port exists in this universe, false otherwise
|
|
*/
|
|
bool Universe::ContainsPort(InputPort *port) const {
|
|
return GenericContainsPort(port, m_input_ports);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if this port is bound to this universe
|
|
* @param port the port to check for
|
|
* @return true if the port exists in this universe, false otherwise
|
|
*/
|
|
bool Universe::ContainsPort(OutputPort *port) const {
|
|
return GenericContainsPort(port, m_output_ports);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a list of input ports associated with this universe
|
|
* @param ports, the vector to be populated
|
|
*/
|
|
void Universe::InputPorts(vector<InputPort*> *ports) {
|
|
ports->clear();
|
|
std::copy(m_input_ports.begin(), m_input_ports.end(),
|
|
std::back_inserter(*ports));
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a list of output ports associated with this universe
|
|
* @param ports, the vector to be populated
|
|
*/
|
|
void Universe::OutputPorts(vector<OutputPort*> *ports) {
|
|
ports->clear();
|
|
std::copy(m_output_ports.begin(), m_output_ports.end(),
|
|
std::back_inserter(*ports));
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a client as a source for this universe
|
|
* @param client the client to add
|
|
*/
|
|
bool Universe::AddSourceClient(Client *client) {
|
|
if (ContainsSourceClient(client))
|
|
return false;
|
|
return AddClient(client, true);
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove a client as a source for this universe
|
|
* @param client the client to remove
|
|
*/
|
|
bool Universe::RemoveSourceClient(Client *client) {
|
|
return RemoveClient(client, true);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if this universe contains a client as a source
|
|
* @param client the client to check for
|
|
* @returns true if this universe contains the client, false otherwise
|
|
*/
|
|
bool Universe::ContainsSourceClient(Client *client) const {
|
|
return find(m_source_clients.begin(), m_source_clients.end(), client) !=
|
|
m_source_clients.end();
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a client as a sink for this universe
|
|
* @param client the client to add
|
|
*/
|
|
bool Universe::AddSinkClient(Client *client) {
|
|
if (ContainsSinkClient(client))
|
|
return false;
|
|
return AddClient(client, false);
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove a client as a sink for this universe
|
|
* @param client the client to remove
|
|
*/
|
|
bool Universe::RemoveSinkClient(Client *client) {
|
|
return RemoveClient(client, false);
|
|
}
|
|
|
|
/*
|
|
* Check if this universe contains a client as a sink
|
|
* @param client the client to check for
|
|
* @returns true if this universe contains the client, false otherwise
|
|
*/
|
|
bool Universe::ContainsSinkClient(Client *client) const {
|
|
return find(m_sink_clients.begin(), m_sink_clients.end(), client) !=
|
|
m_sink_clients.end();
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the dmx data for this universe, this overrides anything from the clients
|
|
* or ports but will be overridden in the next update.
|
|
* @param buffer the dmx buffer with the data
|
|
* @return true is we updated all ports/clients, false otherwise
|
|
*/
|
|
bool Universe::SetDMX(const DmxBuffer &buffer) {
|
|
if (!buffer.Size()) {
|
|
OLA_INFO << "Trying to SetDMX with a 0 length dmx buffer, universe " <<
|
|
UniverseId();
|
|
return true;
|
|
}
|
|
m_buffer.Set(buffer);
|
|
return UpdateDependants();
|
|
}
|
|
|
|
|
|
/*
|
|
* Call this when the dmx in a port that is part of this universe changes
|
|
* @param port the port that has changed
|
|
*/
|
|
bool Universe::PortDataChanged(InputPort *port) {
|
|
if (!ContainsPort(port)) {
|
|
OLA_INFO << "Trying to update a port which isn't bound to universe: "
|
|
<< UniverseId();
|
|
return false;
|
|
}
|
|
if (MergeAll(port, NULL))
|
|
UpdateDependants();
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Called to indicate that data from a client has changed
|
|
*/
|
|
bool Universe::SourceClientDataChanged(Client *client) {
|
|
if (!client)
|
|
return false;
|
|
|
|
AddSourceClient(client); // always add since this may be the first call
|
|
if (MergeAll(NULL, client))
|
|
UpdateDependants();
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle a RDM request for this universe, ownership of the request object is
|
|
* transferred to this method.
|
|
* @returns true if this request was sent to an Output port, false otherwise
|
|
*/
|
|
bool Universe::HandleRDMRequest(InputPort *port,
|
|
const ola::rdm::RDMRequest *request) {
|
|
OLA_INFO << "Got a RDM request for " << request->DestinationUID() <<
|
|
" with command " << std::hex << request->CommandClass() << " and param " <<
|
|
request->ParamId();
|
|
|
|
// populate the input UID map so we know how to route this request later
|
|
m_input_uids[request->SourceUID()] = port;
|
|
|
|
if (m_export_map)
|
|
(*m_export_map->GetUIntMapVar(K_UNIVERSE_RDM_REQUESTS))[
|
|
m_universe_id_str]++;
|
|
|
|
if (request->DestinationUID().IsBroadcast()) {
|
|
// send this request to all ports
|
|
vector<OutputPort*>::iterator port_iter;
|
|
for (port_iter = m_output_ports.begin(); port_iter != m_output_ports.end();
|
|
++port_iter) {
|
|
// because each port deletes the request, we need to copy it here
|
|
(*port_iter)->HandleRDMRequest(request->Duplicate());
|
|
}
|
|
delete request;
|
|
return true;
|
|
|
|
} else {
|
|
map<UID, OutputPort*>::iterator iter =
|
|
m_output_uids.find(request->DestinationUID());
|
|
|
|
if (iter == m_output_uids.end()) {
|
|
OLA_WARN << "Can't find UID " << request->DestinationUID() <<
|
|
" in the output universe map, dropping request";
|
|
delete request;
|
|
return false;
|
|
} else {
|
|
iter->second->HandleRDMRequest(request);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle a RDM response
|
|
*/
|
|
bool Universe::HandleRDMResponse(OutputPort *port,
|
|
const ola::rdm::RDMResponse *response) {
|
|
OLA_INFO << "Got a RDM response for " << response->DestinationUID() <<
|
|
" with command " << std::hex << response->CommandClass() << " and param "
|
|
<< response->ParamId();
|
|
|
|
map<UID, InputPort*>::iterator iter =
|
|
m_input_uids.find(response->DestinationUID());
|
|
|
|
if (m_export_map)
|
|
(*m_export_map->GetUIntMapVar(K_UNIVERSE_RDM_RESPONSES))[
|
|
m_universe_id_str]++;
|
|
|
|
if (iter == m_input_uids.end()) {
|
|
OLA_WARN << "Can't find UID " << response->DestinationUID() <<
|
|
" in the input universe map, dropping response";
|
|
delete response;
|
|
return false;
|
|
} else {
|
|
return iter->second->HandleRDMResponse(response);
|
|
}
|
|
(void) port;
|
|
}
|
|
|
|
|
|
/*
|
|
* Trigger RDM discovery for this universe
|
|
*/
|
|
void Universe::RunRDMDiscovery() {
|
|
OLA_INFO << "RDM discovery triggered for universe " << m_universe_id;
|
|
|
|
vector<OutputPort*>::iterator iter;
|
|
for (iter = m_output_ports.begin(); iter != m_output_ports.end(); ++iter)
|
|
(*iter)->RunRDMDiscovery();
|
|
|
|
// somehow detect when this is done and then send new UIDSets
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns the complete UIDSet for this universe
|
|
*/
|
|
void Universe::GetUIDs(ola::rdm::UIDSet *uids) const {
|
|
map<UID, OutputPort*>::const_iterator iter = m_output_uids.begin();
|
|
for (; iter != m_output_uids.end(); ++iter)
|
|
uids->AddUID(iter->first);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the number of uids in the universe
|
|
*/
|
|
unsigned int Universe::UIDCount() const {
|
|
return m_output_uids.size();
|
|
}
|
|
|
|
|
|
/*
|
|
* Update the UID : port mapping with this new data
|
|
*/
|
|
void Universe::NewUIDList(const ola::rdm::UIDSet &uids, OutputPort *port) {
|
|
map<UID, OutputPort*>::iterator iter = m_output_uids.begin();
|
|
while (iter != m_output_uids.end()) {
|
|
if (iter->second == port && !uids.Contains(iter->first))
|
|
m_output_uids.erase(iter++);
|
|
else
|
|
++iter;
|
|
}
|
|
|
|
ola::rdm::UIDSet::Iterator set_iter = uids.Begin();
|
|
for (; set_iter != uids.End(); ++set_iter) {
|
|
iter = m_output_uids.find(*set_iter);
|
|
if (iter == m_output_uids.end()) {
|
|
m_output_uids[*set_iter] = port;
|
|
} else if (iter->second != port) {
|
|
OLA_WARN << "UID " << *set_iter << " seen on more than one port";
|
|
}
|
|
}
|
|
|
|
if (m_export_map)
|
|
(*m_export_map->GetUIntMapVar(K_UNIVERSE_UID_COUNT_VAR))[m_universe_id_str]
|
|
= m_output_uids.size();
|
|
}
|
|
|
|
|
|
/*
|
|
* Return true if this universe is in use (has at least one port or client).
|
|
*/
|
|
bool Universe::IsActive() const {
|
|
return (m_input_ports.size() || m_output_ports.size() ||
|
|
m_source_clients.size() || m_sink_clients.size());
|
|
}
|
|
|
|
|
|
// Private Methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
/*
|
|
* Called when the dmx data for this universe changes,
|
|
* updates everyone who needs to know (patched ports and network clients)
|
|
*/
|
|
bool Universe::UpdateDependants() {
|
|
vector<OutputPort*>::const_iterator iter;
|
|
set<Client*>::const_iterator client_iter;
|
|
|
|
// write to all ports assigned to this unviverse
|
|
for (iter = m_output_ports.begin(); iter != m_output_ports.end(); ++iter) {
|
|
(*iter)->WriteDMX(m_buffer, m_active_priority);
|
|
}
|
|
|
|
// write to all clients
|
|
for (client_iter = m_sink_clients.begin();
|
|
client_iter != m_sink_clients.end();
|
|
++client_iter) {
|
|
(*client_iter)->SendDMX(m_universe_id, m_buffer);
|
|
}
|
|
|
|
if (m_export_map)
|
|
(*m_export_map->GetUIntMapVar(K_FPS_VAR))[m_universe_id_str]++;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Update the name in the export map.
|
|
*/
|
|
void Universe::UpdateName() {
|
|
if (!m_export_map)
|
|
return;
|
|
StringMap *name_map = m_export_map->GetStringMapVar(K_UNIVERSE_NAME_VAR);
|
|
(*name_map)[m_universe_id_str] = m_universe_name;
|
|
}
|
|
|
|
|
|
/*
|
|
* Update the mode in the export map.
|
|
*/
|
|
void Universe::UpdateMode() {
|
|
if (!m_export_map)
|
|
return;
|
|
StringMap *mode_map = m_export_map->GetStringMapVar(K_UNIVERSE_MODE_VAR);
|
|
(*mode_map)[m_universe_id_str] = (m_merge_mode == Universe::MERGE_LTP ?
|
|
K_MERGE_LTP_STR : K_MERGE_HTP_STR);
|
|
}
|
|
|
|
|
|
/*
|
|
* Add this client to this universe
|
|
* @param client the client to add
|
|
* @pre the client doesn't already exist in the set
|
|
*/
|
|
bool Universe::AddClient(Client *client, bool is_source) {
|
|
set<Client*> &clients = is_source ? m_source_clients : m_sink_clients;
|
|
clients.insert(client);
|
|
OLA_INFO << "Added " << (is_source ? "source" : "sink") << " client, " <<
|
|
client << " to universe " << m_universe_id;
|
|
|
|
if (m_export_map) {
|
|
const string &map_name = is_source ? K_UNIVERSE_SOURCE_CLIENTS_VAR :
|
|
K_UNIVERSE_SINK_CLIENTS_VAR;
|
|
(*m_export_map->GetUIntMapVar(map_name))[m_universe_id_str]++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove this client from the universe. After calling this method you need to
|
|
* check if this universe is still in use, and if not delete it
|
|
* @param client the client to remove
|
|
* @return true is this client was removed, false if it didn't exist
|
|
*/
|
|
bool Universe::RemoveClient(Client *client, bool is_source) {
|
|
set<Client*> &clients = is_source ? m_source_clients : m_sink_clients;
|
|
set<Client*>::iterator iter = find(clients.begin(), clients.end(), client);
|
|
|
|
if (iter == clients.end())
|
|
return false;
|
|
|
|
clients.erase(iter);
|
|
if (m_export_map) {
|
|
const string &map_name = is_source ? K_UNIVERSE_SOURCE_CLIENTS_VAR :
|
|
K_UNIVERSE_SINK_CLIENTS_VAR;
|
|
(*m_export_map->GetUIntMapVar(map_name))[m_universe_id_str]--;
|
|
}
|
|
OLA_INFO << "Client " << client << " has been removed from uni " <<
|
|
m_universe_id;
|
|
|
|
if (!IsActive())
|
|
m_universe_store->AddUniverseGarbageCollection(this);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* HTP Merge all sources (clients/ports)
|
|
* @pre sources.sizez >= 2
|
|
* @param sources the list of DmxSources to merge
|
|
*/
|
|
void Universe::HTPMergeSources(const vector<DmxSource> &sources) {
|
|
vector<DmxSource>::const_iterator iter;
|
|
m_buffer.Reset();
|
|
|
|
for (iter = sources.begin(); iter != sources.end(); ++iter) {
|
|
m_buffer.HTPMerge(iter->Data());
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Merge all port/client sources.
|
|
* This does a priority based merge as documented at:
|
|
* http://opendmx.net/index.php/OLA_Merging_Algorithms
|
|
* @param port the input port that changed or NULL
|
|
* @param client the client that changed or NULL
|
|
* @returns true if the data for this universe changed, false otherwise
|
|
*/
|
|
bool Universe::MergeAll(const InputPort *port, const Client *client) {
|
|
vector<DmxSource> active_sources;
|
|
|
|
vector<InputPort*>::const_iterator iter;
|
|
set<Client*>::const_iterator client_iter;
|
|
|
|
m_active_priority = DmxSource::PRIORITY_MIN;
|
|
TimeStamp now;
|
|
Clock::CurrentTime(&now);
|
|
bool changed_source_is_active = false;
|
|
|
|
// Find the highest active ports
|
|
for (iter = m_input_ports.begin(); iter != m_input_ports.end(); ++iter) {
|
|
DmxSource source = (*iter)->SourceData();
|
|
if (!source.IsSet() || !source.IsActive(now) || !source.Data().Size())
|
|
continue;
|
|
|
|
if (source.Priority() > m_active_priority) {
|
|
changed_source_is_active = false;
|
|
active_sources.clear();
|
|
m_active_priority = source.Priority();
|
|
}
|
|
|
|
if (source.Priority() == m_active_priority) {
|
|
active_sources.push_back(source);
|
|
if (*iter == port)
|
|
changed_source_is_active = true;
|
|
}
|
|
}
|
|
|
|
// find the highest active clients
|
|
for (client_iter = m_source_clients.begin();
|
|
client_iter != m_source_clients.end();
|
|
++client_iter) {
|
|
const DmxSource &source = (*client_iter)->SourceData(UniverseId());
|
|
|
|
if (!source.IsSet() || !source.IsActive(now) || !source.Data().Size())
|
|
continue;
|
|
|
|
if (source.Priority() > m_active_priority) {
|
|
changed_source_is_active = false;
|
|
active_sources.clear();
|
|
m_active_priority = source.Priority();
|
|
}
|
|
|
|
if (source.Priority() == m_active_priority) {
|
|
active_sources.push_back(source);
|
|
if (*client_iter == client)
|
|
changed_source_is_active = true;
|
|
}
|
|
}
|
|
|
|
if (!active_sources.size()) {
|
|
OLA_WARN << "Something changed but we didn't find any active sources " <<
|
|
" for universe " << UniverseId();
|
|
return false;
|
|
}
|
|
|
|
if (!changed_source_is_active)
|
|
// this source didn't have any effect, skip
|
|
return false;
|
|
|
|
// only one source at the active priority
|
|
if (active_sources.size() == 1) {
|
|
m_buffer.Set(active_sources[0].Data());
|
|
} else {
|
|
// multi source merge
|
|
if (m_merge_mode == Universe::MERGE_LTP) {
|
|
vector<DmxSource>::const_iterator source_iter = active_sources.begin();
|
|
DmxSource changed_source;
|
|
if (port)
|
|
changed_source = port->SourceData();
|
|
else
|
|
changed_source = client->SourceData(UniverseId());
|
|
|
|
// check that the current port/client is newer than all other active
|
|
// sources
|
|
for (; source_iter != active_sources.end(); source_iter++) {
|
|
if (changed_source.Timestamp() < source_iter->Timestamp())
|
|
return false;
|
|
}
|
|
// if we made it to here this is the newest source
|
|
m_buffer.Set(changed_source.Data());
|
|
} else {
|
|
HTPMergeSources(active_sources);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an Input or Output port to this universe.
|
|
* @param port, the port to add
|
|
* @param ports, the vector of ports to add to
|
|
*/
|
|
template<class PortClass>
|
|
bool Universe::GenericAddPort(PortClass *port, vector<PortClass*> *ports) {
|
|
if (find(ports->begin(), ports->end(), port) != ports->end())
|
|
return true;
|
|
|
|
ports->push_back(port);
|
|
if (m_export_map) {
|
|
UIntMap *map = m_export_map->GetUIntMapVar(
|
|
IsInputPort<PortClass>() ? K_UNIVERSE_INPUT_PORT_VAR :
|
|
K_UNIVERSE_OUTPUT_PORT_VAR);
|
|
(*map)[m_universe_id_str]++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove an Input or Output port from this universe.
|
|
* @param port, the port to add
|
|
* @param ports, the vector of ports to remove from
|
|
*/
|
|
template<class PortClass>
|
|
bool Universe::GenericRemovePort(PortClass *port,
|
|
vector<PortClass*> *ports,
|
|
map<UID, PortClass*> *uid_map) {
|
|
typename vector<PortClass*>::iterator iter =
|
|
find(ports->begin(), ports->end(), port);
|
|
|
|
if (iter == ports->end()) {
|
|
OLA_DEBUG << "Could not find port " << port->UniqueId() << " in universe "
|
|
<< UniverseId();
|
|
return true;
|
|
}
|
|
|
|
ports->erase(iter);
|
|
if (m_export_map) {
|
|
UIntMap *map = m_export_map->GetUIntMapVar(
|
|
IsInputPort<PortClass>() ? K_UNIVERSE_INPUT_PORT_VAR :
|
|
K_UNIVERSE_OUTPUT_PORT_VAR);
|
|
(*map)[m_universe_id_str]--;
|
|
}
|
|
if (!IsActive())
|
|
m_universe_store->AddUniverseGarbageCollection(this);
|
|
|
|
// Remove any uids that mapped to this port
|
|
typename map<UID, PortClass*>::iterator uid_iter = uid_map->begin();
|
|
while (uid_iter != uid_map->end()) {
|
|
if (uid_iter->second == port)
|
|
uid_map->erase(uid_iter++);
|
|
else
|
|
++uid_iter;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if this universe contains a particular port.
|
|
* @param port, the port to add
|
|
* @param ports, the vector of ports to remove from
|
|
*/
|
|
template<class PortClass>
|
|
bool Universe::GenericContainsPort(PortClass *port,
|
|
const vector<PortClass*> &ports) const {
|
|
return find(ports.begin(), ports.end(), port) != ports.end();
|
|
}
|
|
} // ola
|