fabric/moduleapi.h
fabric/moduleapi.h
Namespaces
Name |
---|
Syntalos |
Classes
Name | |
---|---|
class | Syntalos::ModuleInfo Static information about a module. |
struct | Syntalos::TestSubject The TestSubject struct Data about a test subject. |
class | Syntalos::AbstractStreamPort |
class | Syntalos::VarStreamInputPort |
class | Syntalos::StreamInputPort |
class | Syntalos::StreamOutputPort |
class | Syntalos::AbstractModule Abstract base class for all modules. |
Defines
Name | |
---|---|
SYNTALOS_DECLARE_MODULE | |
SYNTALOS_MODULE(MI) |
Macros Documentation
define SYNTALOS_DECLARE_MODULE
#define SYNTALOS_DECLARE_MODULE _Pragma("GCC visibility push(default)") extern "C" ModuleInfo *syntalos_module_info(); \
extern "C" const char *syntalos_module_api_id(); \
_Pragma("GCC visibility pop")
Define interfaces for a dynamically loaded Syntalos module, so we can find it at runtime.
define SYNTALOS_MODULE
#define SYNTALOS_MODULE(
MI
)
ModuleInfo *syntalos_module_info() \
{ \
return new MI##Info; \
} \
const char *syntalos_module_api_id() \
{ \
return SY_MODULE_API_TAG; \
}
Define interfaces for a dynamically loaded Syntalos module, so we can find it at runtime.
Source code
/*
* Copyright (C) 2016-2024 Matthias Klumpp <matthias@tenstral.net>
*
* Licensed under the GNU Lesser General Public License Version 3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the license, or
* (at your option) any later version.
*
* This software 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MODULEAPI_H
#define MODULEAPI_H
#include <QByteArray>
#include <QDebug>
#include <QList>
#include <QObject>
#include <QIcon>
#include "modconfig.h"
#include "optionalwaitcondition.h"
#include "streams/stream.h"
#include "datactl/datatypes.h"
#include "datactl/edlstorage.h"
#include "datactl/syclock.h"
#include "datactl/timesync.h"
namespace Syntalos
{
class AbstractModule;
class StreamOutputPort;
enum class ModuleFeature {
NONE = 0,
SHOW_SETTINGS = 1 << 0,
SHOW_DISPLAY = 1 << 1,
REALTIME = 1 << 2,
CALL_UI_EVENTS = 1 << 3,
REQUEST_CPU_AFFINITY =
1 << 4,
PROHIBIT_CPU_AFFINITY = 1
<< 5
};
Q_DECLARE_FLAGS(ModuleFeatures, ModuleFeature)
Q_DECLARE_OPERATORS_FOR_FLAGS(ModuleFeatures)
enum class ModuleDriverKind {
NONE,
THREAD_DEDICATED,
EVENTS_DEDICATED,
EVENTS_SHARED
};
enum class UsbHotplugEventKind {
NONE,
DEVICE_ARRIVED,
DEVICE_LEFT,
DEVICES_CHANGE
};
enum class ModuleCategory : uint32_t {
NONE = 0,
SYNTALOS_DEV = 1 << 0,
EXAMPLES = 1 << 1,
DEVICES = 1 << 2,
GENERATORS = 1 << 3,
SCRIPTING = 1 << 4,
DISPLAY = 1 << 5,
WRITERS = 1 << 6,
PROCESSING = 1 << 7
};
Q_DECLARE_FLAGS(ModuleCategories, ModuleCategory)
Q_DECLARE_OPERATORS_FOR_FLAGS(ModuleCategories)
QString toString(ModuleCategory category);
ModuleCategory moduleCategoryFromString(const QString &categoryStr);
ModuleCategories moduleCategoriesFromString(const QString &categoriesStr);
enum class ModuleModifier {
NONE = 0,
ENABLED = 1 << 0,
STOP_ON_FAILURE = 1 << 1,
};
Q_DECLARE_FLAGS(ModuleModifiers, ModuleModifier)
Q_DECLARE_OPERATORS_FOR_FLAGS(ModuleModifiers)
inline uint qHash(ModuleModifier key, uint seed)
{
return ::qHash(static_cast<int>(key), seed);
}
class Q_DECL_EXPORT ModuleInfo
{
friend class Engine;
public:
explicit ModuleInfo();
virtual ~ModuleInfo();
virtual QString id() const;
virtual QString name() const;
virtual QString summary() const;
virtual QString description() const;
virtual QString authors() const;
virtual QString license() const;
virtual QIcon icon() const;
virtual void refreshIcon();
virtual QColor color() const;
virtual ModuleCategories categories() const;
virtual QString storageGroupName() const;
virtual bool singleton() const;
virtual AbstractModule *createModule(QObject *parent = nullptr) = 0;
int count() const;
QString rootDir() const;
void setRootDir(const QString &dir);
protected:
void setIcon(const QIcon &icon);
private:
Q_DISABLE_COPY(ModuleInfo)
class Private;
QScopedPointer<Private> d;
void setCount(int count);
};
#define SYNTALOS_DECLARE_MODULE \
_Pragma("GCC visibility push(default)") extern "C" ModuleInfo *syntalos_module_info(); \
extern "C" const char *syntalos_module_api_id(); \
_Pragma("GCC visibility pop")
#define SYNTALOS_MODULE(MI) \
ModuleInfo *syntalos_module_info() \
{ \
return new MI##Info; \
} \
const char *syntalos_module_api_id() \
{ \
return SY_MODULE_API_TAG; \
}
struct Q_DECL_EXPORT TestSubject {
QString id;
QString group;
bool active;
QString comment;
QVariant data;
};
enum class PortDirection {
NONE,
INPUT,
OUTPUT
};
class Q_DECL_EXPORT AbstractStreamPort
{
public:
virtual ~AbstractStreamPort() = default;
virtual QString id() const = 0;
virtual QString title() const = 0;
virtual PortDirection direction() const
{
return PortDirection::NONE;
}
virtual int dataTypeId() const = 0;
virtual QString dataTypeName() const = 0;
virtual AbstractModule *owner() const = 0;
};
class Q_DECL_EXPORT VarStreamInputPort : public AbstractStreamPort
{
public:
explicit VarStreamInputPort(AbstractModule *owner, const QString &id, const QString &title);
virtual ~VarStreamInputPort();
virtual bool acceptsSubscription(const QString &typeName) = 0;
bool hasSubscription() const;
void setSubscription(StreamOutputPort *src, std::shared_ptr<VariantStreamSubscription> sub);
void resetSubscription();
StreamOutputPort *outPort() const;
std::shared_ptr<VariantStreamSubscription> subscriptionVar();
QString id() const override;
QString title() const override;
PortDirection direction() const override;
AbstractModule *owner() const override;
protected:
std::optional<std::shared_ptr<VariantStreamSubscription>> m_sub;
private:
Q_DISABLE_COPY(VarStreamInputPort)
class Private;
QScopedPointer<Private> d;
};
template<typename T>
class StreamInputPort : public VarStreamInputPort
{
public:
explicit StreamInputPort(AbstractModule *owner, const QString &id, const QString &title)
: VarStreamInputPort(owner, id, title)
{
m_acceptedTypeId = syDataTypeId<T>();
m_acceptedTypeName = BaseDataType::typeIdToString(m_acceptedTypeId);
}
std::shared_ptr<StreamSubscription<T>> subscription()
{
auto sub = std::dynamic_pointer_cast<StreamSubscription<T>>(m_sub.value());
if (sub == nullptr) {
if (hasSubscription()) {
qCritical().noquote() << "Conversion of variant subscription to dedicated type" << typeid(T).name()
<< "failed."
<< "Modules are connected in a way they shouldn't be (terminating now).";
qFatal("Bad module connection.");
assert(0);
} else {
qWarning().noquote() << "Tried to obtain" << typeid(T).name()
<< "subscription from a port that was not subscribed to anything.";
}
}
return sub;
}
int dataTypeId() const override
{
return m_acceptedTypeId;
}
QString dataTypeName() const override
{
return m_acceptedTypeName;
}
bool acceptsSubscription(const QString &typeName) override
{
return m_acceptedTypeName == typeName;
}
private:
int m_acceptedTypeId;
QString m_acceptedTypeName;
};
class Q_DECL_EXPORT StreamOutputPort : public AbstractStreamPort
{
public:
explicit StreamOutputPort(
AbstractModule *owner,
const QString &id,
const QString &title,
std::shared_ptr<VariantDataStream> stream);
virtual ~StreamOutputPort();
bool canSubscribe(const QString &typeName);
int dataTypeId() const override;
QString dataTypeName() const override;
template<typename T>
std::shared_ptr<DataStream<T>> stream()
{
return std::dynamic_pointer_cast<DataStream<T>>(streamVar());
}
std::shared_ptr<VariantDataStream> streamVar();
std::shared_ptr<VariantStreamSubscription> subscribe();
void startStream();
void stopStream();
QString id() const override;
QString title() const override;
PortDirection direction() const override;
AbstractModule *owner() const override;
private:
Q_DISABLE_COPY(StreamOutputPort)
class Private;
QScopedPointer<Private> d;
};
VariantDataStream *newStreamForType(int typeId);
VarStreamInputPort *newInputPortForType(int typeId, AbstractModule *mod, const QString &id, const QString &title);
using intervalEventFunc_t = void (AbstractModule::*)(int &);
using recvDataEventFunc_t = void (AbstractModule::*)();
class Q_DECL_EXPORT AbstractModule : public QObject
{
Q_OBJECT
friend class Engine;
friend class MLinkModule;
public:
explicit AbstractModule(QObject *parent = nullptr);
explicit AbstractModule(const QString &id, QObject *parent = nullptr);
virtual ~AbstractModule();
ModuleState state() const;
void setStateDormant();
void setStateReady();
QString id() const;
int index() const;
virtual QString name() const;
virtual void setName(const QString &name);
virtual ModuleDriverKind driver() const;
virtual ModuleFeatures features() const;
template<typename T>
requires std::is_base_of_v<BaseDataType, T>
std::shared_ptr<DataStream<T>> registerOutputPort(const QString &id, const QString &title = QString())
{
if (m_outPorts.contains(id)) {
qWarning().noquote() << "Module" << name() << "already registered an output port with ID:" << id;
return m_outPorts[id]->stream<T>();
}
std::shared_ptr<DataStream<T>> stream(new DataStream<T>());
std::shared_ptr<StreamOutputPort> outPort(new StreamOutputPort(this, id, title, stream));
stream->setCommonMetadata(this->id(), name(), title);
m_outPorts.insert(id, outPort);
Q_EMIT portConfigurationUpdated();
return stream;
}
template<typename T>
requires std::is_base_of_v<BaseDataType, T>
std::shared_ptr<StreamInputPort<T>> registerInputPort(const QString &id, const QString &title = QString())
{
if (m_inPorts.contains(id)) {
qWarning().noquote() << "Module" << name() << "already registered an input port with ID:" << id;
return std::dynamic_pointer_cast<StreamInputPort<T>>(m_inPorts[id]);
}
std::shared_ptr<StreamInputPort<T>> inPort(new StreamInputPort<T>(this, id, title));
m_inPorts.insert(id, inPort);
Q_EMIT portConfigurationUpdated();
return inPort;
}
std::shared_ptr<VariantDataStream> registerOutputPortByTypeId(
int typeId,
const QString &id,
const QString &title = QString())
{
if (m_outPorts.contains(id)) {
qWarning().noquote() << "Module" << name() << "already registered an output port with ID:" << id;
return m_outPorts[id]->streamVar();
}
auto varStream = newStreamForType(typeId);
if (varStream == nullptr)
return nullptr;
std::shared_ptr<VariantDataStream> stream(varStream);
std::shared_ptr<StreamOutputPort> outPort(new StreamOutputPort(this, id, title, stream));
stream->setCommonMetadata(this->id(), name(), title);
m_outPorts.insert(id, outPort);
Q_EMIT portConfigurationUpdated();
return stream;
}
std::shared_ptr<VarStreamInputPort> registerInputPortByTypeId(
int typeId,
const QString &id,
const QString &title = QString())
{
if (m_inPorts.contains(id)) {
qWarning().noquote() << "Module" << name() << "already registered an input port with ID:" << id;
return m_inPorts[id];
}
auto varInPort = newInputPortForType(typeId, this, id, title);
if (varInPort == nullptr)
return nullptr;
std::shared_ptr<VarStreamInputPort> inPort(varInPort);
m_inPorts.insert(id, inPort);
Q_EMIT portConfigurationUpdated();
return inPort;
}
virtual bool initialize();
virtual bool prepare(const TestSubject &testSubject) = 0;
virtual void start();
virtual void runThread(OptionalWaitCondition *startWaitCondition);
virtual void processUiEvents();
virtual void stop();
virtual void finalize();
virtual void showDisplayUi();
virtual bool isDisplayUiVisible();
virtual void showSettingsUi();
virtual bool isSettingsUiVisible();
virtual void hideDisplayUi();
virtual void hideSettingsUi();
virtual void serializeSettings(const QString &confBaseDir, QVariantHash &settings, QByteArray &extraData);
virtual bool loadSettings(const QString &confBaseDir, const QVariantHash &settings, const QByteArray &extraData);
virtual void inputPortConnected(VarStreamInputPort *inPort);
virtual void updateStartWaitCondition(OptionalWaitCondition *waitCondition);
QString lastError() const;
QString moduleRootDir() const;
void setEventsMaxModulesPerThread(int maxModuleCount);
int eventsMaxModulesPerThread() const;
void clearInPorts();
void clearOutPorts();
void removeInPortById(const QString &id);
void removeOutPortById(const QString &id);
QList<std::shared_ptr<VarStreamInputPort>> inPorts() const;
QList<std::shared_ptr<StreamOutputPort>> outPorts() const;
std::shared_ptr<VarStreamInputPort> inPortById(const QString &id) const;
std::shared_ptr<StreamOutputPort> outPortById(const QString &id) const;
QList<QPair<intervalEventFunc_t, int>> intervalEventCallbacks() const;
QList<QPair<recvDataEventFunc_t, std::shared_ptr<VariantStreamSubscription>>> recvDataEventCallbacks() const;
QVariant serializeDisplayUiGeometry();
void restoreDisplayUiGeometry(const QVariant &var);
void setTimer(std::shared_ptr<SyncTimer> timer);
ModuleModifiers modifiers() const;
void setModifiers(ModuleModifiers modifiers);
Q_SIGNALS:
void stateChanged(ModuleState state);
void error(const QString &message);
void statusMessage(const QString &message);
void nameChanged(const QString &name);
void portsConnected(const VarStreamInputPort *inPort, const StreamOutputPort *outPort);
void portConfigurationUpdated();
void synchronizerDetailsChanged(
const QString &id,
const Syntalos::TimeSyncStrategies &strategies,
const Syntalos::microseconds_t &tolerance);
void synchronizerOffsetChanged(const QString &id, const Syntalos::microseconds_t ¤tOffset);
void modifiersUpdated();
protected:
void raiseError(const QString &message);
void setStatusMessage(const QString &message);
bool makeDirectory(const QString &dir);
void appProcessEvents();
QString datasetNameSuggestion(bool lowercase = true) const;
QString datasetNameFromSubMetadata(const QVariantHash &subMetadata) const;
QString datasetNameFromParameters(const QString &preferredName, const QVariantHash &subMetadata) const;
QString dataBasenameFromSubMetadata(
const QVariantHash &subMetadata,
const QString &defaultName = QStringLiteral("data"));
std::shared_ptr<EDLDataset> createDefaultDataset(
const QString &preferredName = QString(),
const QVariantHash &subMetadata = QVariantHash());
std::shared_ptr<EDLDataset> createDatasetInGroup(
std::shared_ptr<EDLGroup> group,
const QString &preferredName = QString(),
const QVariantHash &subMetadata = QVariantHash());
std::shared_ptr<EDLDataset> getDefaultDataset();
std::shared_ptr<EDLDataset> getDatasetInGroup(
std::shared_ptr<EDLGroup> group,
const QString &preferredName = QString(),
const QVariantHash &subMetadata = QVariantHash());
std::shared_ptr<EDLGroup> createStorageGroup(const QString &groupName);
QWidget *addDisplayWindow(QWidget *window, bool owned = true);
QWidget *addSettingsWindow(QWidget *window, bool owned = true);
template<typename T>
void registerTimedEvent(void (T::*fn)(int &), const milliseconds_t &interval)
{
static_assert(
std::is_base_of<AbstractModule, T>::value,
"Callback needs to point to a member function of a class derived from AbstractModule");
const auto amFn = static_cast<intervalEventFunc_t>(fn);
m_intervalEventCBList.append(qMakePair(amFn, interval.count()));
}
template<typename T>
void registerDataReceivedEvent(void (T::*fn)(), std::shared_ptr<VariantStreamSubscription> subscription)
{
static_assert(
std::is_base_of<AbstractModule, T>::value,
"Callback needs to point to a member function of a class derived from AbstractModule");
const auto amFn = static_cast<recvDataEventFunc_t>(fn);
m_recvDataEventCBList.append(qMakePair(amFn, subscription));
}
void clearDataReceivedEventRegistrations();
std::unique_ptr<FreqCounterSynchronizer> initCounterSynchronizer(double frequencyHz);
std::unique_ptr<SecondaryClockSynchronizer> initClockSynchronizer(double expectedFrequencyHz = 0);
uint potentialNoaffinityCPUCount() const;
int defaultRealtimePriority() const;
bool isEphemeralRun() const;
virtual void usbHotplugEvent(UsbHotplugEventKind kind);
void setInitialized();
bool initialized() const;
std::atomic_bool m_running;
std::shared_ptr<SyncTimer> m_syTimer;
private:
Q_DISABLE_COPY(AbstractModule)
class Private;
std::unique_ptr<Private> d;
QMap<QString, std::shared_ptr<StreamOutputPort>> m_outPorts;
QMap<QString, std::shared_ptr<VarStreamInputPort>> m_inPorts;
QList<QPair<intervalEventFunc_t, int>> m_intervalEventCBList;
QList<QPair<recvDataEventFunc_t, std::shared_ptr<VariantStreamSubscription>>> m_recvDataEventCBList;
void setState(ModuleState state);
void setId(const QString &id);
void setIndex(int index);
void setSimpleStorageNames(bool enabled);
void setStorageGroup(std::shared_ptr<EDLGroup> edlGroup);
void resetEventCallbacks();
void setPotentialNoaffinityCPUCount(uint coreN);
void setDefaultRTPriority(int prio);
void setEphemeralRun(bool isEphemeral);
};
} // namespace Syntalos
#endif // MODULEAPI_H
Updated on 2024-11-06 at 17:10:29 +0000