Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add event dashboards and improve dashboard handling #21

Merged
merged 5 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,19 @@ qt_add_resources(appOPC_UA_Browser "icons"
PREFIX "/"
FILES font/Swansea.ttf
font/SwanseaBold.ttf
icons/keyboard_arrow_down.svg
icons/arrow_right.svg
icons/keyboard_arrow_up.svg
icons/back.svg
icons/bolt.svg
icons/cancel.svg
icons/checkmark.svg
icons/clear_all.svg
icons/connect.svg
icons/dashboard.svg
icons/delete.svg
icons/disconnect.svg
icons/edit.svg
icons/event.svg
icons/expert.svg
icons/forward.svg
icons/info.svg
Expand Down
178 changes: 150 additions & 28 deletions src/backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ void BackEnd::connectToEndpoint(int endpointIndex, bool usePassword, const QStri
const QString &password)
{
if ((endpointIndex < 0) || (endpointIndex >= mEndpointList.size())) {
setState(QStringLiteral("endpoint index out of range"));
setState(tr("endpoint index out of range"));
qCCritical(backendLog)
<< QStringLiteral("endpoint index out of range, index: %1, endpoint list size: %2")
.arg(endpointIndex)
Expand Down Expand Up @@ -319,7 +319,8 @@ void BackEnd::disconnectFromEndpoint()
mOpcUaClient->disconnectFromEndpoint();
}

void BackEnd::monitorNode(MonitoredItemModel *model, const QString &nodeId)
void BackEnd::monitorNode(MonitoredItemModel *model, const QString &nodeId,
const std::optional<QOpcUaMonitoringParameters::EventFilter> &eventFilter)
{
if ((model == nullptr) || model->containsItem(nodeId))
return;
Expand All @@ -338,12 +339,15 @@ void BackEnd::monitorNode(MonitoredItemModel *model, const QString &nodeId)
return;
}

model->addItem(node);
if (!eventFilter.has_value())
model->addItem(node);
else
model->addEventItem(node, eventFilter.value());
}

void BackEnd::connectToEndpoint()
{
setState(QStringLiteral("connected to client \"%1\"")
setState(tr("connect with security policy \"%1\"")
.arg(mConnectionConfiguration.mEndpoint.securityPolicy()));

createClient();
Expand All @@ -366,12 +370,40 @@ void BackEnd::monitorSelectedNodes()
if (monitoredItemModel == nullptr)
return;

const QStringList nodeIdList = mOpcUaModel->selectedNodes();
for (const auto &nodeId : nodeIdList.toList()) {
monitorNode(monitoredItemModel, nodeId);
if (mDashboardItemModel->getCurrentDashboardType() == DashboardItem::DashboardType::Events) {
if (mSelectedEventSourceNodes.isEmpty())
return;

QOpcUaMonitoringParameters::EventFilter filter;

for (const auto &index : mOpcUaModel->selectedIndices()) {
const auto item = mOpcUaModel->itemForIndex(index);
if (!item)
continue;

const auto operand = item->calculateBrowsePathToEventType();
if (!operand.has_value())
continue;

filter << operand.value();
}

for (const auto &nodeId : mSelectedEventSourceNodes) {
monitorNode(monitoredItemModel, nodeId, filter);
}
} else {
const QStringList nodeIdList = mOpcUaModel->selectedNodes();
for (const auto &nodeId : nodeIdList.toList()) {
monitorNode(monitoredItemModel, nodeId);
}
}
}

void BackEnd::cacheSelectedEventSourceNodes()
{
mSelectedEventSourceNodes = mOpcUaModel->selectedNodes();
}

void BackEnd::saveCurrentDashboard(const QString &name)
{
Q_ASSERT(mDashboardItemModel);
Expand All @@ -397,11 +429,22 @@ void BackEnd::saveCurrentDashboard(const QString &name)
nodeIds);
addItemToStringListModel(mSavedVariableDashboardsModel, name);
break;
case DashboardItem::DashboardType::Events:
case DashboardItem::DashboardType::Events: {
settings.setValue(Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % name,
nodeIds);

QList<QList<QOpcUaSimpleAttributeOperand>> selectClauses;
const auto eventFilters = monitoredItemModel->eventFilters();
for (const auto &filter : eventFilters)
selectClauses.push_back(filter.selectClauses());

settings.setValue(Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % name
% QChar::fromLatin1('/') % Constants::SettingsKey::EventFilters,
QVariant::fromValue(selectClauses));

addItemToStringListModel(mSavedEventDashboardsModel, name);
break;
}
default:
Q_UNREACHABLE();
break;
Expand All @@ -420,6 +463,20 @@ void BackEnd::removeSavedVariableDashboard(const QString &name)
removeItemFromStringListModel(mSavedVariableDashboardsModel, name);
}

void BackEnd::removeSavedEventDashboard(const QString &name)
{
Q_ASSERT(mDashboardItemModel);

if (name.isEmpty())
return;

QSettings settings;
settings.remove(Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % name);
settings.remove(Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % name
% QChar::fromLatin1('/') % Constants::SettingsKey::EventFilters);
removeItemFromStringListModel(mSavedEventDashboardsModel, name);
}

void BackEnd::loadDashboard(const QString &name)
{
Q_ASSERT(mDashboardItemModel);
Expand All @@ -428,12 +485,34 @@ void BackEnd::loadDashboard(const QString &name)
if (monitoredItemModel == nullptr)
return;

const QString variableCandidate =
Constants::SettingsKey::DashboardsVariables % QChar::fromLatin1('/') % name;
const QString eventCandidate =
Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % name;

QSettings settings;
const QStringList nodeIds = settings.value(Constants::SettingsKey::DashboardsVariables
% QChar::fromLatin1('/') % name)
.toStringList();
for (const auto &nodeId : nodeIds) {
monitorNode(monitoredItemModel, nodeId);
QStringList nodeIds = settings.value(variableCandidate).toStringList();
if (!nodeIds.empty()) {
for (const auto &nodeId : nodeIds) {
monitorNode(monitoredItemModel, nodeId);
}
} else {
nodeIds = settings.value(eventCandidate).toStringList();
if (!nodeIds.isEmpty()) {
const auto selectClauses = settings.value(eventCandidate % QChar::fromLatin1('/')
% Constants::SettingsKey::EventFilters)
.value<QList<QList<QOpcUaSimpleAttributeOperand>>>();
if (!selectClauses.isEmpty()) {
for (int i = 0; i < nodeIds.size(); ++i) {
if (selectClauses.size() - 1 < i || selectClauses.at(i).isEmpty())
continue;

QOpcUaMonitoringParameters::EventFilter filter;
filter.setSelectClauses(selectClauses.at(i));
monitorNode(monitoredItemModel, nodeIds.at(i), filter);
}
}
}
}
}

Expand Down Expand Up @@ -473,11 +552,43 @@ void BackEnd::renameSavedVariableDashboard(const QString &previousName, const QS
mDashboardItemModel->renameItem(previousName, newName);
}

void BackEnd::renameSavedEventDashboard(const QString &previousName, const QString &newName)
{
QSettings settings;
const QString settingsGroupName =
Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % previousName;

if (previousName == newName || !mSavedEventDashboardsModel->stringList().contains(previousName)
|| mSavedEventDashboardsModel->stringList().contains(newName)
|| !settings.contains(settingsGroupName))
return;

const auto nodeIds = settings.value(settingsGroupName).toStringList();
const auto eventFilter = settings.value(settingsGroupName % QChar::fromLatin1('/')
+ Constants::SettingsKey::EventFilters);

removeSavedEventDashboard(previousName);

settings.setValue(Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % newName,
nodeIds);
settings.setValue(Constants::SettingsKey::DashboardsEvents % QChar::fromLatin1('/') % newName
% QChar::fromLatin1('/') % Constants::SettingsKey::EventFilters,
eventFilter);
addItemToStringListModel(mSavedEventDashboardsModel, newName);

mDashboardItemModel->renameItem(previousName, newName);
}

bool BackEnd::hasSavedVariableDashboard(const QString &name) const
{
return mSavedVariableDashboardsModel->stringList().contains(name);
}

bool BackEnd::hasSavedEventDashboard(const QString &name) const
{
return mSavedEventDashboardsModel->stringList().contains(name);
}

int BackEnd::instantiateCompanionSpecVariableDashboard(const QString &name)
{
assert(mDashboardItemModel);
Expand All @@ -500,7 +611,7 @@ int BackEnd::instantiateCompanionSpecVariableDashboard(const QString &name)
void BackEnd::findServers(const QString &urlString)
{
QUrl url(urlString);
setState(QStringLiteral("Discovering servers on \"%1\"").arg(urlString));
setState(tr("Discovering servers on \"%1\"").arg(urlString));

createClient();
// set default port if missing
Expand All @@ -519,11 +630,11 @@ void BackEnd::findServersComplete(const QList<QOpcUaApplicationDescription> &ser
{
if (!isSuccessStatus(statusCode)) {
qCWarning(backendLog) << "servers detection failed " << statusCode;
setState(QStringLiteral("servers detection failed"));
setState(tr("servers detection failed"));
return;
}

const QString state = QStringLiteral("%1 server(s) detected").arg(servers.size());
const QString state = tr("%1 server(s) detected").arg(servers.size());
qCDebug(backendLog) << state;
setState(state);
saveServerHost(mHostUrl.toString());
Expand All @@ -540,7 +651,7 @@ void BackEnd::findServersComplete(const QList<QOpcUaApplicationDescription> &ser
void BackEnd::getEndpoints(int serverIndex)
{
if ((serverIndex < 0) || (serverIndex >= mServerList.size())) {
setState(QStringLiteral("server index out of range"));
setState(tr("server index out of range"));
qCCritical(backendLog)
<< QStringLiteral("server index out of range, index: %1, server list size: %2")
.arg(serverIndex)
Expand All @@ -554,7 +665,7 @@ void BackEnd::getEndpoints(int serverIndex)
void BackEnd::requestEndpoints(const QString &serverUrl)
{
mServerUrl = serverUrl;
setState(QStringLiteral("Request endpoints for \"%1\"").arg(mServerUrl.toString()));
setState(tr("Request endpoints for \"%1\"").arg(mServerUrl.toString()));
qCDebug(backendLog) << "Request endpoints for " << mServerUrl.toString();
createClient();
mOpcUaClient->requestEndpoints(mServerUrl.toString());
Expand All @@ -571,11 +682,11 @@ void BackEnd::getEndpointsComplete(const QList<QOpcUaEndpointDescription> &endpo
return;
}

setState(QStringLiteral("request of endpoints failed"));
setState(tr("request of endpoints failed"));
return;
}

const QString state = QStringLiteral("%1 endpoint(s) received").arg(endpoints.size());
const QString state = tr("%1 endpoint(s) received").arg(endpoints.size());
qCDebug(backendLog) << state;
setState(state);
mEndpointList = endpoints;
Expand All @@ -602,7 +713,7 @@ void BackEnd::getEndpointsComplete(const QList<QOpcUaEndpointDescription> &endpo
void BackEnd::clientConnected()
{
qCDebug(backendLog) << "client connected";
setState(QStringLiteral("client connected"));
setState(tr("client connected"));

connect(mOpcUaClient, &QOpcUaClient::namespaceArrayUpdated, this,
&BackEnd::namespacesArrayUpdated);
Expand All @@ -612,7 +723,7 @@ void BackEnd::clientConnected()
void BackEnd::clientDisconnected()
{
qCDebug(backendLog) << "client disconnected";
setState(QStringLiteral("client disconnected"));
setState(tr("client disconnected"));

saveLastDashboards();
mDashboardItemModel->clearItems();
Expand Down Expand Up @@ -642,13 +753,13 @@ void BackEnd::namespacesArrayUpdated(const QStringList &namespaceArray)
void BackEnd::clientError(QOpcUaClient::ClientError error)
{
qCDebug(backendLog) << "client error:" << error;
setState(QStringLiteral("client error: %1").arg(error));
setState(tr("client error: %1").arg(error));
}

void BackEnd::clientState(QOpcUaClient::ClientState state)
{
qCDebug(backendLog) << "client state:" << state;
setState(QStringLiteral("client state changed: %1").arg(state));
setState(tr("client state changed: %1").arg(state));
}

void BackEnd::clientConnectError(QOpcUaErrorState *errorState)
Expand All @@ -675,8 +786,7 @@ void BackEnd::createClient()
if (!mOpcUaClient) {
const QString message(tr("A possible cause could be that the backend "
"could not be loaded as a plugin."));
setState(QStringLiteral("Failed to connect to server") % QChar::fromLatin1('\n')
% message);
setState(tr("Failed to connect to server") % QChar::fromLatin1('\n') % message);
return;
}

Expand Down Expand Up @@ -759,8 +869,20 @@ void BackEnd::loadLastDashboardsFromSettings()
if (model != nullptr) {
const QStringList nodeIds =
settings.value(Constants::SettingsKey::NodeIds).toStringList();
for (const auto &nodeId : nodeIds) {
monitorNode(model, nodeId);
const auto selectClauses = settings.value(Constants::SettingsKey::EventFilters)
.value<QList<QList<QOpcUaSimpleAttributeOperand>>>();

for (int j = 0; j < nodeIds.size(); ++j) {
if (type == DashboardItem::DashboardType::Events) {
QOpcUaMonitoringParameters::EventFilter filter;
if (selectClauses.size() - 1 < j || selectClauses.at(j).isEmpty())
continue;

filter.setSelectClauses(selectClauses.at(j));
monitorNode(model, nodeIds.at(j), filter);
} else {
monitorNode(model, nodeIds.at(j));
}
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,18 @@ class BackEnd : public QObject
Q_INVOKABLE void disconnectFromEndpoint();

Q_INVOKABLE void monitorSelectedNodes();
Q_INVOKABLE void cacheSelectedEventSourceNodes();

Q_INVOKABLE void saveCurrentDashboard(const QString &name);
Q_INVOKABLE void removeSavedVariableDashboard(const QString &name);
Q_INVOKABLE void removeSavedEventDashboard(const QString &name);
Q_INVOKABLE void loadDashboard(const QString &name);
Q_INVOKABLE int instantiateDefaultVariableDashboard(const QString &name);
Q_INVOKABLE void renameSavedVariableDashboard(const QString &previousName,
const QString &newName);
Q_INVOKABLE void renameSavedEventDashboard(const QString &previousName, const QString &newName);
Q_INVOKABLE bool hasSavedVariableDashboard(const QString &name) const;
Q_INVOKABLE bool hasSavedEventDashboard(const QString &name) const;

Q_INVOKABLE void loadLastDashboardsFromSettings();

Expand Down Expand Up @@ -241,7 +245,9 @@ private slots:
void setupPkiConfiguration();
void setState(const QString &state);

void monitorNode(MonitoredItemModel *model, const QString &nodeId);
void
monitorNode(MonitoredItemModel *model, const QString &nodeId,
const std::optional<QOpcUaMonitoringParameters::EventFilter> &eventFilter = {});

void requestEndpoints(const QString &serverUrl);
void connectToEndpoint(const QOpcUaEndpointDescription &endpoint, bool usePassword,
Expand Down Expand Up @@ -295,6 +301,8 @@ private slots:

QVector<CompanionSpecDevice> mCompanionSpecDevices;
QHash<QString, QStringList> mCompanionSpecVariableDashboards;

QStringList mSelectedEventSourceNodes;
};

#endif // BACKEND_H
1 change: 1 addition & 0 deletions src/constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const QString SettingsKey::DashboardsEvents = QStringLiteral("dashboards/events"
const QString SettingsKey::Name = QStringLiteral("name");
const QString SettingsKey::Type = QStringLiteral("type");
const QString SettingsKey::NodeIds = QStringLiteral("nodeIDs");
const QString SettingsKey::EventFilters = QStringLiteral("eventFilters");
const QString SettingsKey::RecentConnections = QStringLiteral("recentConnections");
const QString SettingsKey::Url = QStringLiteral("url");
const QString SettingsKey::Language = QStringLiteral("language");
Expand Down
Loading
Loading