一、Supra配置文件
其实这一段和TBB本身的联系并不大,讲它略有些鸡肋。但是不分析它又不好分析后面的节点的动态管理,所以还是拉过来一起分析一下。在前面其实简单介绍过这一个模块,它主要是通过配置文件的读写操作来动态处理节点的参数传递。同时,在Supra中支持实时的对参数的修改,这个显得尤为突出。
其配置管理文件主要为XML类型,在XML的节点中,定义上节点的名称、类型和ID。并且在其子项中定义了相关的参数及参数的值。而在Supra程序内自动通过tinyxml调用来解析这个配置文件获取得相关的信息(包括但不限于上面提到的参数等)。配置文件中还支持定义Supra类型节点的连接状态的定义和相关参数处理,这样就可以直接进行整个项目中节点的数据流向和分析的控制。
二、Supra对配置文件的管理
Supra中对配置文件的管理分成两大块,即节点的管理和配置的动态处理。
1、节点的管理
节点的管理类似于静态的定义节点的名称等用来生成实例和进行节点间的连接。在程序中基本分为普通节点的读写、输入节点的读写和输出节点的读写以及后面的连接控制读写操作。这样就形成了整体认知上的静态管理。
2、配置的动态处理
这个更多的是配置文件选项的自我管理,提供了不同数据类型的处理及相关参数发生变化后动态通知程序进行更新。
三、动态管理配置文件
节点的动态加载这一块基本已经分析过了,如果想要查看更细节的代码可以下载其源码进行分析即可。这里重点分析一下其对配置文件的动态管理这部分。从应用层次上看,其为分三个层次:
1、节点应用
//抽象基类
//This is ugly, but "queueing" and "rejecting" are enum-values in old versions and types in newer versions...
#if TBB_INTERFACE_VERSION_MAJOR >= 9
#define TBB_QUEUE_RESOLVER(__buffering__) typename std::conditional<__buffering__, tbb::flow::queueing, tbb::flow::rejecting>::type
#else
#define TBB_QUEUE_RESOLVER(__buffering__) (__buffering__ ? tbb::flow::queueing : tbb::flow::rejecting)
#endif //TBB_INTERFACE_VERSION_MAJOR >= 9
namespace supra
{
class RecordObject;
}
namespace supra
{
/*! \brief Abstract interface for a general node (input, output or processing).
*
* This is the common interface for all nodes.
*/
class AbstractNode
{
......
public:
/// Base constructor for all nodes
AbstractNode(const std::string & nodeID, bool queueing)
: m_nodeID(nodeID)
, m_queueing(queueing)
{
m_configurationDictionary.setValueRangeDictionary(&m_valueRangeDictionary);
}
......
/// Returns a const pointer to the \see ValueRangeDictionary of this node
/// The ValueRangeDictionary describes the parameters of the node and
/// their valid ranges
const ValueRangeDictionary * getValueRangeDictionary()
{
return &m_valueRangeDictionary;
}
/// Returns a const pointer to the \see ConfigurationDictionary of this node
/// The ConfigurationDictionary contains the parameters currently set
/// and their values
const ConfigurationDictionary * getConfigurationDictionary()
{
return &m_configurationDictionary;
}
/// Returns the ID of the node.
/// Node IDs are unique and cannot be changed after creation.
const std::string& getNodeID()
{
return m_nodeID;
}
/// Templated interface to change a node parameter
/// returns whether the value was valid
template<typename ValueType>
bool changeConfig(const std::string& configKey, const ValueType& newValue)
{
if (m_valueRangeDictionary.hasKey(configKey) &&
m_valueRangeDictionary.isInRange(configKey, newValue))
{
logging::log_parameter("Parameter: ", m_nodeID, ".", configKey, " = ", newValue);
m_configurationDictionary.set(configKey, newValue);
configurationEntryChanged(configKey);
return true;
}
return false;
}
/// Function to set the whole \see ConfiguraitonDictionary at once
/// Only parameters whose value is valid are applied
void changeConfig(const ConfigurationDictionary& newConfig)
{
// trigger callback for major configuration changes in overloaded implementation
configurationDictionaryChanged(newConfig);
//validate the configuration entries
ConfigurationDictionary validConfig = newConfig;
validConfig.setValueRangeDictionary(&m_valueRangeDictionary);
validConfig.checkEntriesAndLog(m_nodeID);
//store all valid entries
m_configurationDictionary = validConfig;
configurationChanged();
}
/// Returns a string with the timing info (call frequency and run-time)
/// if the node uses the \see CallFrequency member to monitor itself
std::string getTimingInfo()
{
return m_callFrequency.getTimingInfo();
}
protected:
/// The collection of node parameters
ConfigurationDictionary m_configurationDictionary;
/// The definition of parameters and their respective ranges
ValueRangeDictionary m_valueRangeDictionary;
/// \see CallFrequency can be used by the node implementation to monitor its
/// timings (frequency of activation and run-time)
CallFrequency m_callFrequency;
bool m_queueing;
protected:
/// Callback for the node implementation to be notified of the change of parameters.
/// Needs to be overwritten and thread-safe
virtual void configurationEntryChanged(const std::string& configKey) {
};
/// Callback for the node implementation to be notified of the change of parameters.
/// Needs to be overwritten and thread-safe
virtual void configurationChanged() {
};
/// Callback for the node implementation to be notified of the change of a full dictionary change.
/// can be be overwritten but must be thread-safe
/// dictionary does not contain fail-safe mechanisms yet, thus should be only used to adjust value range changes or add new settings on the fly
virtual void configurationDictionaryChanged(const ConfigurationDictionary& newConfig) {
};
private:
std::string m_nodeID;
};
}
#endif //!__ABSTRACTNODE_H__
InputDevice::InputDevice(tbb::flow::graph& graph, const std::string & nodeID)
: AbstractInput(graph, nodeID,1)
, m_sequenceIndex(0)
, m_frameIndex(0)
, m_numel(0)
, m_frozen(false)
, m_lastFrame(false)
, m_ready(false)
{
m_callFrequency.setName("RawMhd");
//Setup allowed values for parameters
m_valueRangeDictionary.set<bool>("singleImage", {
true, false }, false, "Single image");
m_valueRangeDictionary.set<bool>("streamSequenceOnce", {
true, false }, false, "Emit sequences once");
m_valueRangeDictionary.set<double>("frequency", 0.001, 100, 5, "Frequency");
m_valueRangeDictionary.set<std::string>("mhdMetaDataFilename", "", "User