fabric/moduleapi.cpp
fabric/moduleapi.cpp
Defines
Name | |
---|---|
CHECK_RETURN_INPUT_PORT(T) | |
CHECK_RETURN_STREAM(T) |
Macros Documentation
define CHECK_RETURN_INPUT_PORT
#define CHECK_RETURN_INPUT_PORT(
T
)
if (typeId == BaseDataType::T) \
return new StreamInputPort<T>(mod, id, title);
define CHECK_RETURN_STREAM
#define CHECK_RETURN_STREAM(
T
)
if (typeId == BaseDataType::T) \
return new DataStream<T>();
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/>.
*/
#include "moduleapi.h"
#include "config.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QMainWindow>
#include <QMessageBox>
#include <QStandardPaths>
#include <QCursor>
#include "datactl/frametype.h"
#include "utils/misc.h"
using namespace Syntalos;
class ModuleInfo::Private
{
public:
Private() {}
~Private() {}
int count;
QString rootDir;
QIcon icon;
};
ModuleInfo::ModuleInfo()
: d(new ModuleInfo::Private)
{
d->count = 0;
}
ModuleInfo::~ModuleInfo() {}
QString ModuleInfo::id() const
{
return QStringLiteral("unknown");
}
QString ModuleInfo::name() const
{
return QStringLiteral("Unknown Module");
}
QString ModuleInfo::summary() const
{
const auto desc = description();
if (desc.length() <= 80)
return desc;
return desc.left(80) + QStringLiteral("...");
}
QString ModuleInfo::description() const
{
return QStringLiteral("An unknown description.");
}
QString ModuleInfo::authors() const
{
return QString();
}
QString ModuleInfo::license() const
{
return QString();
}
QIcon ModuleInfo::icon() const
{
if (d->icon.isNull())
return QIcon(":/module/generic");
return d->icon;
}
void ModuleInfo::setIcon(const QIcon &icon)
{
d->icon = icon;
}
void ModuleInfo::refreshIcon()
{
QIcon icon = QIcon(":/module/generic");
if (!d->rootDir.isEmpty()) {
const auto iconExts = QStringList() << ".svg"
<< ".svgz"
<< ".png";
for (const auto &iconExt : iconExts) {
const auto iconFname = QDir(d->rootDir).filePath(id() + iconExt);
if (QFileInfo::exists(iconFname)) {
icon = QIcon(iconFname);
break;
}
}
}
d->icon = icon;
}
QColor ModuleInfo::color() const
{
auto img = icon().pixmap(32, 32).toImage().convertToFormat(QImage::Format_ARGB32);
if (img.isNull())
return Qt::lightGray;
int redBucket = 0;
int greenBucket = 0;
int blueBucket = 0;
int totalColorCount = 0;
auto bits = img.constBits();
for (int y = 0, h = img.height(); y < h; y++) {
for (int x = 0, w = img.width(); x < w; x++) {
QRgb color = ((uint *)bits)[x + y * w];
if (qAlpha(color) < 100)
continue;
const auto red = qRed(color);
const auto green = qGreen(color);
const auto blue = qBlue(color);
// try to ignore colors too close to white or black
if ((abs(red - green) < 38) && (abs(green - blue) < 38))
continue;
redBucket += red;
greenBucket += green;
blueBucket += blue;
totalColorCount++;
}
}
// return a gray/black-ish color in case everything was black
// or has no usable colors
if (totalColorCount == 0)
return QColor::fromRgb(77, 77, 77);
return QColor::fromRgb(redBucket / totalColorCount, greenBucket / totalColorCount, blueBucket / totalColorCount);
}
ModuleCategories ModuleInfo::categories() const
{
return ModuleCategory::NONE;
}
QString ModuleInfo::storageGroupName() const
{
return QString();
}
bool ModuleInfo::singleton() const
{
return false;
}
int ModuleInfo::count() const
{
return d->count;
}
QString ModuleInfo::rootDir() const
{
return d->rootDir;
}
void ModuleInfo::setCount(int count)
{
d->count = count;
}
void ModuleInfo::setRootDir(const QString &dir)
{
d->rootDir = dir;
refreshIcon();
}
class VarStreamInputPort::Private
{
public:
Private() {}
~Private() {}
QString id;
QString title;
AbstractModule *owner;
StreamOutputPort *outPort;
};
VarStreamInputPort::VarStreamInputPort(AbstractModule *owner, const QString &id, const QString &title)
: d(new VarStreamInputPort::Private)
{
d->id = id;
d->title = title;
d->owner = owner;
d->outPort = nullptr;
}
VarStreamInputPort::~VarStreamInputPort() {}
bool VarStreamInputPort::hasSubscription() const
{
return m_sub.has_value();
}
void VarStreamInputPort::setSubscription(StreamOutputPort *src, std::shared_ptr<VariantStreamSubscription> sub)
{
d->outPort = src;
m_sub = sub;
d->owner->inputPortConnected(this);
// signal interested parties as the input module that new
// ports were connected
emit d->owner->portsConnected(this, src);
}
void VarStreamInputPort::resetSubscription()
{
if (m_sub.has_value())
m_sub.value()->unsubscribe();
m_sub.reset();
d->outPort = nullptr;
}
StreamOutputPort *VarStreamInputPort::outPort() const
{
if (hasSubscription())
return d->outPort;
return nullptr;
}
std::shared_ptr<VariantStreamSubscription> VarStreamInputPort::subscriptionVar()
{
auto sub = m_sub.value();
if (sub == nullptr) {
qCritical().noquote()
<< "Tried to obtain variant subscription from a port that was not subscribed to anything.";
}
return sub;
}
QString VarStreamInputPort::id() const
{
return d->id;
}
QString VarStreamInputPort::title() const
{
return d->title;
}
PortDirection VarStreamInputPort::direction() const
{
return PortDirection::INPUT;
}
AbstractModule *VarStreamInputPort::owner() const
{
return d->owner;
}
class StreamOutputPort::Private
{
public:
Private() {}
~Private() {}
QString id;
QString title;
std::shared_ptr<VariantDataStream> stream;
AbstractModule *owner;
};
StreamOutputPort::StreamOutputPort(
AbstractModule *owner,
const QString &id,
const QString &title,
std::shared_ptr<VariantDataStream> stream)
: d(new StreamOutputPort::Private)
{
d->id = id;
d->title = title;
d->owner = owner;
d->stream = stream;
}
StreamOutputPort::~StreamOutputPort() {}
bool StreamOutputPort::canSubscribe(const QString &typeName)
{
return typeName == d->stream->dataTypeName();
}
int StreamOutputPort::dataTypeId() const
{
return d->stream->dataTypeId();
}
QString StreamOutputPort::dataTypeName() const
{
return d->stream->dataTypeName();
}
std::shared_ptr<VariantDataStream> StreamOutputPort::streamVar()
{
return d->stream;
}
std::shared_ptr<VariantStreamSubscription> StreamOutputPort::subscribe()
{
return d->stream->subscribeVar();
}
void StreamOutputPort::stopStream()
{
if (d->stream->active())
d->stream->stop();
}
void StreamOutputPort::startStream()
{
d->stream->start();
}
QString StreamOutputPort::id() const
{
return d->id;
}
QString StreamOutputPort::title() const
{
return d->title;
}
PortDirection StreamOutputPort::direction() const
{
return PortDirection::OUTPUT;
}
AbstractModule *StreamOutputPort::owner() const
{
return d->owner;
}
#define CHECK_RETURN_INPUT_PORT(T) \
if (typeId == BaseDataType::T) \
return new StreamInputPort<T>(mod, id, title);
#define CHECK_RETURN_STREAM(T) \
if (typeId == BaseDataType::T) \
return new DataStream<T>();
VarStreamInputPort *Syntalos::newInputPortForType(
int typeId,
AbstractModule *mod,
const QString &id,
const QString &title = QString())
{
CHECK_RETURN_INPUT_PORT(ControlCommand)
CHECK_RETURN_INPUT_PORT(TableRow)
CHECK_RETURN_INPUT_PORT(FirmataControl)
CHECK_RETURN_INPUT_PORT(FirmataData)
CHECK_RETURN_INPUT_PORT(Frame)
CHECK_RETURN_INPUT_PORT(IntSignalBlock)
CHECK_RETURN_INPUT_PORT(FloatSignalBlock)
qCritical() << "Unable to create input port for unknown type ID" << typeId;
return nullptr;
}
VariantDataStream *Syntalos::newStreamForType(int typeId)
{
CHECK_RETURN_STREAM(ControlCommand)
CHECK_RETURN_STREAM(TableRow)
CHECK_RETURN_STREAM(FirmataControl)
CHECK_RETURN_STREAM(FirmataData)
CHECK_RETURN_STREAM(Frame)
CHECK_RETURN_STREAM(IntSignalBlock)
CHECK_RETURN_STREAM(FloatSignalBlock)
qCritical() << "Unable to create data stream for unknown type ID" << typeId;
return nullptr;
}
QString Syntalos::toString(ModuleCategory category)
{
switch (category) {
case ModuleCategory::SYNTALOS_DEV:
return "sydevel";
case ModuleCategory::EXAMPLES:
return "examples";
case ModuleCategory::DEVICES:
return "devices";
case ModuleCategory::GENERATORS:
return "generators";
case ModuleCategory::SCRIPTING:
return "scripting";
case ModuleCategory::DISPLAY:
return "display";
case ModuleCategory::WRITERS:
return "writers";
case ModuleCategory::PROCESSING:
return "processing";
default:
return "none";
}
}
ModuleCategory Syntalos::moduleCategoryFromString(const QString &categoryStr)
{
static const QMap<QString, ModuleCategory> categoryMap = {
{"none", ModuleCategory::NONE },
{"sydevel", ModuleCategory::SYNTALOS_DEV},
{"example", ModuleCategory::EXAMPLES },
{"device", ModuleCategory::DEVICES },
{"generator", ModuleCategory::GENERATORS },
{"scripting", ModuleCategory::SCRIPTING },
{"display", ModuleCategory::DISPLAY },
{"writers", ModuleCategory::WRITERS },
{"processing", ModuleCategory::PROCESSING }
};
return categoryMap.value(categoryStr, ModuleCategory::NONE);
}
ModuleCategories Syntalos::moduleCategoriesFromString(const QString &categoriesStr)
{
ModuleCategories categories = ModuleCategory::NONE;
auto catsList = categoriesStr.split(';', Qt::SkipEmptyParts);
for (const QString &name : catsList)
categories |= moduleCategoryFromString(name);
return categories;
}
class AbstractModule::Private
{
public:
Private()
: state(ModuleState::INITIALIZING),
modIndex(0),
simpleStorageNames(true),
initialized(false)
{
}
~Private() {}
std::atomic<ModuleState> state;
QString id;
QString name;
QString lastError;
int modIndex;
uint potentialNoaffinityCPUCount;
int defaultRealtimePriority;
static int s_eventsMaxModulesPerThread;
QList<QPair<QWidget *, bool>> displayWindows;
QList<QPair<QWidget *, bool>> settingsWindows;
bool simpleStorageNames;
std::shared_ptr<EDLGroup> rootDataGroup;
std::shared_ptr<EDLDataset> defaultDataset;
bool initialized;
bool runIsEmphemeral;
ModuleModifiers modifiers;
};
// instantiate static field
int AbstractModule::Private::s_eventsMaxModulesPerThread = -1;
AbstractModule::AbstractModule(QObject *parent)
: QObject(parent),
m_running(false),
d(new AbstractModule::Private)
{
d->modifiers = ModuleModifier::ENABLED | ModuleModifier::STOP_ON_FAILURE;
d->id = QStringLiteral("unknown");
d->name = QStringLiteral("Unknown Module");
d->s_eventsMaxModulesPerThread = -1;
d->runIsEmphemeral = false;
}
AbstractModule::AbstractModule(const QString &id, QObject *parent)
: AbstractModule(parent)
{
setId(id);
}
AbstractModule::~AbstractModule()
{
// delete windows if we own them
for (auto &wp : d->displayWindows) {
if (wp.second)
delete wp.first;
}
for (auto &wp : d->settingsWindows) {
if (wp.second)
delete wp.first;
}
}
ModuleState AbstractModule::state() const
{
return d->state;
}
void AbstractModule::setStateDormant()
{
if ((d->state == ModuleState::RUNNING) || (d->state == ModuleState::INITIALIZING))
setState(ModuleState::DORMANT);
}
void AbstractModule::setStateReady()
{
if (d->state == ModuleState::PREPARING)
setState(ModuleState::READY);
}
QString AbstractModule::id() const
{
return d->id;
}
int AbstractModule::index() const
{
return d->modIndex;
}
QString AbstractModule::name() const
{
return d->name;
}
void AbstractModule::setName(const QString &name)
{
d->name = simplifyStrForModuleName(name);
for (auto &oport : outPorts()) {
oport->streamVar()->setCommonMetadata(d->id, d->name, oport->title());
}
emit nameChanged(d->name);
}
ModuleDriverKind AbstractModule::driver() const
{
return ModuleDriverKind::NONE;
}
ModuleFeatures AbstractModule::features() const
{
return ModuleFeature::SHOW_DISPLAY | ModuleFeature::SHOW_SETTINGS;
}
bool AbstractModule::initialize()
{
assert(!initialized());
setInitialized();
return true;
}
void AbstractModule::start()
{
m_running = true;
setState(ModuleState::RUNNING);
}
void AbstractModule::runThread(OptionalWaitCondition *waitCondition)
{
// prevent blocking forever if a threaded module is started without overriding this method
waitCondition->wait(this);
}
void AbstractModule::processUiEvents()
{
/* do nothing */
}
void AbstractModule::stop()
{
m_running = false;
if (d->state != ModuleState::ERROR)
setState(ModuleState::IDLE);
}
void AbstractModule::finalize()
{
/* do nothing */
}
void AbstractModule::showDisplayUi()
{
const bool onlyOne = d->displayWindows.size() == 1;
for (auto const wp : d->displayWindows) {
if (onlyOne)
wp.first->setWindowTitle(name());
wp.first->show();
wp.first->raise();
}
}
bool AbstractModule::isDisplayUiVisible()
{
for (auto const wp : d->displayWindows) {
if (wp.first->isVisible())
return true;
}
return false;
}
void AbstractModule::showSettingsUi()
{
const bool onlyOne = d->settingsWindows.size() == 1;
for (auto const wp : d->settingsWindows) {
if (onlyOne)
wp.first->setWindowTitle(QStringLiteral("%1 - Settings").arg(name()));
// set an initial position if we do not have one yet
if (wp.first->pos().isNull()) {
auto pos = QCursor::pos();
pos.setY(pos.y() - (wp.first->height() / 2));
wp.first->move(pos);
}
wp.first->show();
wp.first->raise();
}
}
bool AbstractModule::isSettingsUiVisible()
{
for (auto const wp : d->settingsWindows) {
if (wp.first->isVisible())
return true;
}
return false;
}
void AbstractModule::hideDisplayUi()
{
for (auto const wp : d->displayWindows)
wp.first->hide();
}
void AbstractModule::hideSettingsUi()
{
for (auto const wp : d->settingsWindows)
wp.first->hide();
}
void AbstractModule::serializeSettings(const QString &, QVariantHash &, QByteArray &)
{
/* do nothing */
}
bool AbstractModule::loadSettings(const QString &, const QVariantHash &, const QByteArray &)
{
return true;
}
void AbstractModule::inputPortConnected(VarStreamInputPort *)
{
/* do nothing */
}
void AbstractModule::updateStartWaitCondition(OptionalWaitCondition *)
{
/* do nothing */
}
QString AbstractModule::lastError() const
{
return d->lastError;
}
QString AbstractModule::moduleRootDir() const
{
auto moduleDir = QStringLiteral("%1/../modules/%2").arg(QCoreApplication::applicationDirPath(), id());
QFileInfo checkModDir(moduleDir);
if (moduleDir.startsWith("/usr/") || !checkModDir.exists())
moduleDir = QStringLiteral("%1/%2").arg(SY_MODULESDIR, id());
const auto origModuleDir = moduleDir;
QFileInfo fi(moduleDir);
moduleDir = fi.canonicalFilePath();
return moduleDir.isEmpty() ? origModuleDir : moduleDir;
}
void AbstractModule::setEventsMaxModulesPerThread(int maxModuleCount)
{
d->s_eventsMaxModulesPerThread = maxModuleCount;
}
int AbstractModule::eventsMaxModulesPerThread() const
{
return d->s_eventsMaxModulesPerThread;
}
void AbstractModule::clearInPorts()
{
m_inPorts.clear();
emit portConfigurationUpdated();
}
void AbstractModule::clearOutPorts()
{
m_outPorts.clear();
emit portConfigurationUpdated();
}
void AbstractModule::removeInPortById(const QString &id)
{
m_inPorts.remove(id);
emit portConfigurationUpdated();
}
void AbstractModule::removeOutPortById(const QString &id)
{
m_outPorts.remove(id);
emit portConfigurationUpdated();
}
QList<std::shared_ptr<VarStreamInputPort>> AbstractModule::inPorts() const
{
return m_inPorts.values();
}
QList<std::shared_ptr<StreamOutputPort>> AbstractModule::outPorts() const
{
return m_outPorts.values();
}
std::shared_ptr<VarStreamInputPort> AbstractModule::inPortById(const QString &id) const
{
return m_inPorts.value(id);
}
std::shared_ptr<StreamOutputPort> AbstractModule::outPortById(const QString &id) const
{
return m_outPorts.value(id);
}
QList<QPair<intervalEventFunc_t, int>> AbstractModule::intervalEventCallbacks() const
{
return m_intervalEventCBList;
}
QList<QPair<recvDataEventFunc_t, std::shared_ptr<VariantStreamSubscription>>> AbstractModule::recvDataEventCallbacks()
const
{
return m_recvDataEventCBList;
}
bool AbstractModule::makeDirectory(const QString &dir)
{
if (!QDir().mkpath(dir)) {
raiseError(QStringLiteral("Unable to create directory '%1'.").arg(dir));
return false;
}
return true;
}
void AbstractModule::appProcessEvents()
{
qApp->processEvents();
}
QString AbstractModule::datasetNameSuggestion(bool lowercase) const
{
auto rawName = name();
if (lowercase)
rawName = rawName.toLower();
QString datasetName;
if (d->simpleStorageNames)
datasetName = simplifyStrForFileBasenameLower(rawName);
else
datasetName = simplifyStrForFileBasename(rawName);
// this check should never fail, the dataset name should never consist only
// of unsuitable characters - but just in ase it does, we safeguard against that
if (datasetName.isEmpty())
return createRandomString(8);
return datasetName;
}
QString AbstractModule::datasetNameFromSubMetadata(const QVariantHash &subMetadata) const
{
const auto srcModNameKey = _commonMetadataKeyMap->value(CommonMetadataKey::SrcModName);
const auto dataNameProposalKey = _commonMetadataKeyMap->value(CommonMetadataKey::DataNameProposal);
auto dataName = subMetadata.value(dataNameProposalKey).toString();
if (dataName.isEmpty())
dataName = subMetadata.value(srcModNameKey, datasetNameSuggestion()).toString();
else {
if (dataName.contains('/')) {
const auto parts = qStringSplitLimit(dataName, '/', 1);
dataName = parts[0];
} else {
dataName = subMetadata.value(srcModNameKey).toString();
}
}
if (dataName.isEmpty())
dataName = datasetNameSuggestion();
else if (d->simpleStorageNames)
dataName = simplifyStrForFileBasenameLower(dataName);
return dataName;
}
QString AbstractModule::dataBasenameFromSubMetadata(const QVariantHash &subMetadata, const QString &defaultName)
{
auto dataName = subMetadata.value(_commonMetadataKeyMap->value(CommonMetadataKey::DataNameProposal)).toString();
if (dataName.contains('/')) {
const auto parts = qStringSplitLimit(dataName, '/', 1);
dataName = parts[1];
} else {
dataName = defaultName;
}
if (dataName.isEmpty())
dataName = defaultName;
if (d->simpleStorageNames)
dataName = simplifyStrForFileBasenameLower(dataName);
return dataName;
}
QString AbstractModule::datasetNameFromParameters(const QString &preferredName, const QVariantHash &subMetadata) const
{
QString datasetName;
// if we have subscription metadata, try to use that data to determine the
// data set name
if (!subMetadata.isEmpty()) {
datasetName = datasetNameFromSubMetadata(subMetadata);
}
// attempt to use the preferred name (if we have one, and don't already have a dataset name from subscription
// metadata)
if (datasetName.isEmpty() && !preferredName.isEmpty()) {
if (d->simpleStorageNames)
datasetName = simplifyStrForFileBasenameLower(preferredName);
else
datasetName = simplifyStrForFileBasename(preferredName);
}
// just set our module name if we still have no data set name
if (datasetName.isEmpty())
datasetName = datasetNameSuggestion();
return datasetName;
}
std::shared_ptr<EDLDataset> AbstractModule::createDefaultDataset(
const QString &preferredName,
const QVariantHash &subMetadata)
{
if (d->defaultDataset.get() != nullptr)
return d->defaultDataset;
if (d->rootDataGroup.get() == nullptr) {
qCritical().noquote().nospace()
<< "Module \"" << name()
<< "\" tried to obtain its default dataset, but no root storage group has been set yet.";
return nullptr;
}
d->defaultDataset = createDatasetInGroup(d->rootDataGroup, preferredName, subMetadata);
return d->defaultDataset;
}
std::shared_ptr<EDLDataset> AbstractModule::createDatasetInGroup(
std::shared_ptr<EDLGroup> group,
const QString &preferredName,
const QVariantHash &subMetadata)
{
const auto datasetName = datasetNameFromParameters(preferredName, subMetadata);
// check if the dataset already exists
auto dset = group->datasetByName(datasetName);
if (dset.get() != nullptr) {
if (!dset->isEmpty()) {
raiseError(QStringLiteral("Tried to use dataset '%1' for storage, but the dataset was already in use. "
"Please ensure unique names for data storage!")
.arg(datasetName));
return nullptr;
}
}
return group->datasetByName(datasetName, true);
}
std::shared_ptr<EDLDataset> AbstractModule::getDefaultDataset()
{
return d->defaultDataset;
}
std::shared_ptr<EDLDataset> AbstractModule::getDatasetInGroup(
std::shared_ptr<EDLGroup> group,
const QString &preferredName,
const QVariantHash &subMetadata)
{
const auto datasetName = datasetNameFromParameters(preferredName, subMetadata);
return group->datasetByName(datasetName);
}
std::shared_ptr<EDLGroup> AbstractModule::createStorageGroup(const QString &groupName)
{
if (d->rootDataGroup.get() == nullptr) {
qCritical().noquote() << "Module" << name()
<< "tried to create a new storage group, but no root storage group has been set yet.";
return nullptr;
}
return d->rootDataGroup->groupByName(groupName, true);
}
QWidget *AbstractModule::addDisplayWindow(QWidget *window, bool owned)
{
d->displayWindows.append(qMakePair(window, owned));
return window;
}
QWidget *AbstractModule::addSettingsWindow(QWidget *window, bool owned)
{
d->settingsWindows.append(qMakePair(window, owned));
return window;
}
void AbstractModule::clearDataReceivedEventRegistrations()
{
m_recvDataEventCBList.clear();
}
std::unique_ptr<FreqCounterSynchronizer> AbstractModule::initCounterSynchronizer(double frequencyHz)
{
if ((d->state != ModuleState::PREPARING) && (d->state != ModuleState::READY) && (d->state != ModuleState::RUNNING))
return nullptr;
assert(frequencyHz > 0);
auto synchronizer = std::make_unique<FreqCounterSynchronizer>(m_syTimer, name(), frequencyHz);
synchronizer->setNotifyCallbacks(
[this](const QString &id, const TimeSyncStrategies &strategies, const microseconds_t &tolerance) {
Q_EMIT synchronizerDetailsChanged(id, strategies, tolerance);
},
[this](const QString &id, const microseconds_t ¤tOffset) {
Q_EMIT synchronizerOffsetChanged(id, currentOffset);
});
return synchronizer;
}
std::unique_ptr<SecondaryClockSynchronizer> AbstractModule::initClockSynchronizer(double expectedFrequencyHz)
{
if ((d->state != ModuleState::PREPARING) && (d->state != ModuleState::READY) && (d->state != ModuleState::RUNNING))
return nullptr;
auto synchronizer = std::make_unique<SecondaryClockSynchronizer>(m_syTimer, name());
if (expectedFrequencyHz > 0)
synchronizer->setExpectedClockFrequencyHz(expectedFrequencyHz);
synchronizer->setNotifyCallbacks(
[this](const QString &id, const TimeSyncStrategies &strategies, const microseconds_t &tolerance) {
Q_EMIT synchronizerDetailsChanged(id, strategies, tolerance);
},
[this](const QString &id, const microseconds_t ¤tOffset) {
Q_EMIT synchronizerOffsetChanged(id, currentOffset);
});
return synchronizer;
}
uint AbstractModule::potentialNoaffinityCPUCount() const
{
return d->potentialNoaffinityCPUCount;
}
int AbstractModule::defaultRealtimePriority() const
{
return d->defaultRealtimePriority;
}
bool AbstractModule::isEphemeralRun() const
{
return d->runIsEmphemeral;
}
void AbstractModule::usbHotplugEvent(UsbHotplugEventKind kind)
{
/* do nothing */
}
void AbstractModule::setInitialized()
{
if (d->initialized)
return;
d->initialized = true;
setState(ModuleState::IDLE);
}
bool AbstractModule::initialized() const
{
return d->initialized;
}
QVariant AbstractModule::serializeDisplayUiGeometry()
{
QVariantHash obj;
for (int i = 0; i < d->displayWindows.size(); i++) {
const auto wp = d->displayWindows.at(i);
QVariantHash info;
info.insert("visible", wp.first->isVisible());
info.insert("geometry", QString::fromUtf8(wp.first->saveGeometry().toBase64()));
const auto mw = qobject_cast<QMainWindow *>(wp.first);
if (mw != nullptr)
info.insert("state", QString::fromUtf8(mw->saveState().toBase64()));
obj.insert(QString::number(i), info);
}
return obj;
}
void AbstractModule::restoreDisplayUiGeometry(const QVariant &var)
{
const auto info = var.toHash();
if (info.isEmpty())
return;
for (int i = 0; i < d->displayWindows.size(); i++) {
const auto wp = d->displayWindows.at(i);
auto winfo = info.value(QString::number(i)).toHash();
if (winfo.isEmpty())
continue;
if (winfo.value("visible").toBool())
wp.first->show();
else
wp.first->hide();
const auto b64Geometry = winfo.value("geometry").toString();
wp.first->restoreGeometry(QByteArray::fromBase64(b64Geometry.toUtf8()));
const auto mw = qobject_cast<QMainWindow *>(wp.first);
if (mw != nullptr) {
const auto b64State = winfo.value("state").toString();
mw->restoreState(QByteArray::fromBase64(b64State.toUtf8()));
}
}
}
void AbstractModule::setState(ModuleState state)
{
d->state = state;
emit stateChanged(state);
}
void AbstractModule::raiseError(const QString &message)
{
// if there is multiple errors emitted, likely caused by the first one, we only bubble up the first one
if (d->state == ModuleState::ERROR) {
qCritical().noquote().nospace() << "Not escalating subsequent error from module '" << name()
<< "': " << message;
return;
}
d->lastError = message;
setState(ModuleState::ERROR);
qCritical().noquote().nospace() << "Error raised by module '" << name() << "': " << message;
emit error(message);
}
void AbstractModule::setId(const QString &id)
{
d->id = id;
}
void AbstractModule::setIndex(int index)
{
d->modIndex = index;
}
void AbstractModule::setSimpleStorageNames(bool enabled)
{
d->simpleStorageNames = enabled;
}
void AbstractModule::setStorageGroup(std::shared_ptr<EDLGroup> edlGroup)
{
d->defaultDataset.reset();
d->rootDataGroup = edlGroup;
}
void AbstractModule::resetEventCallbacks()
{
m_intervalEventCBList.clear();
}
void AbstractModule::setPotentialNoaffinityCPUCount(uint coreN)
{
d->potentialNoaffinityCPUCount = coreN;
}
void AbstractModule::setDefaultRTPriority(int prio)
{
d->defaultRealtimePriority = prio;
}
void AbstractModule::setEphemeralRun(bool isEphemeral)
{
d->runIsEmphemeral = isEphemeral;
}
void AbstractModule::setStatusMessage(const QString &message)
{
emit statusMessage(message);
}
void AbstractModule::setTimer(std::shared_ptr<SyncTimer> timer)
{
m_syTimer = timer;
}
ModuleModifiers Syntalos::AbstractModule::modifiers() const
{
return d->modifiers;
}
void Syntalos::AbstractModule::setModifiers(ModuleModifiers modifiers)
{
d->modifiers = modifiers;
Q_EMIT modifiersUpdated();
}
Updated on 2024-11-06 at 17:10:29 +0000