Waterpump Example▲
Sélectionnez
/**
**************************************************************************
**
** Copyright (C) 2018 basysKom GmbH, opensource@basyskom.com
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the QtOpcUa module.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
***************************************************************************
*/
#include
"opcuamachinebackend.h"
#include <QtQml>
OpcUaMachineBackend::
OpcUaMachineBackend(QObject *
parent)
:
QObject(parent)
, m_percentFilledTank1(0
)
, m_percentFilledTank2(0
)
, m_tank2TargetPercent(0
)
, m_tank2ValveState(false
)
, m_machineState(MachineState::
Idle)
, m_connected(false
)
, m_message("Ready to connect"
)
, m_successfullyCreated(false
)
{
qRegisterMetaType&
lt;OpcUaMachineBackend::
MachineState&
gt;();
qmlRegisterType&
lt;OpcUaMachineBackend&
gt;("OpcUaMachineBackend"
, 1
, 0
, "OpcUaMachineBackend"
);
QOpcUaProvider provider;
setBackends(provider.availableBackends());
}
OpcUaMachineBackend::
~
OpcUaMachineBackend()
{
if
(m_client &
amp;&
amp; m_client-&
gt;state() ==
QOpcUaClient::
Connected)
m_client-&
gt;disconnectFromEndpoint();
}
void
OpcUaMachineBackend::
clientStateHandler(QOpcUaClient::
ClientState state)
{
m_connected =
(state ==
QOpcUaClient::ClientState::
Connected);
emit connectedChanged(m_connected);
if
(state ==
QOpcUaClient::ClientState::
Connected) {
setMessage("Connected"
);
// Create node objects for reading, writing and subscriptions
m_machineNode.reset(m_client-&
gt;node("ns=2;s=Machine"
));
m_machineStateNode.reset(m_client-&
gt;node("ns=2;s=Machine.State"
));
m_percentFilledTank1Node.reset(m_client-&
gt;node("ns=2;s=Machine.Tank1.PercentFilled"
));
m_percentFilledTank2Node.reset(m_client-&
gt;node("ns=2;s=Machine.Tank2.PercentFilled"
));
m_tank2TargetPercentNode.reset(m_client-&
gt;node("ns=2;s=Machine.Tank2.TargetPercent"
));
m_tank2ValveStateNode.reset(m_client-&
gt;node("ns=2;s=Machine.Tank2.ValveState"
));
m_machineDesignationNode.reset(m_client-&
gt;node("ns=2;s=Machine.Designation"
));
// Connect signal handlers for subscribed values
QObject::
connect(m_machineStateNode.data(), &
amp;QOpcUaNode::
dataChangeOccurred, this
, &
amp;OpcUaMachineBackend::
machineStateUpdated);
QObject::
connect(m_percentFilledTank1Node.data(), &
amp;QOpcUaNode::
dataChangeOccurred, this
, &
amp;OpcUaMachineBackend::
percentFilledTank1Updated);
QObject::
connect(m_percentFilledTank2Node.data(), &
amp;QOpcUaNode::
dataChangeOccurred, this
, &
amp;OpcUaMachineBackend::
percentFilledTank2Updated);
QObject::
connect(m_tank2TargetPercentNode.data(), &
amp;QOpcUaNode::
dataChangeOccurred, this
, &
amp;OpcUaMachineBackend::
tank2TargetPercentUpdated);
QObject::
connect(m_tank2ValveStateNode.data(), &
amp;QOpcUaNode::
dataChangeOccurred, this
, &
amp;OpcUaMachineBackend::
tank2ValveStateUpdated);
// Subscribe to data changes
m_machineStateNode-&
gt;enableMonitoring(QOpcUa::NodeAttribute::
Value, QOpcUaMonitoringParameters(100
));
m_percentFilledTank1Node-&
gt;enableMonitoring(QOpcUa::NodeAttribute::
Value, QOpcUaMonitoringParameters(100
));
m_percentFilledTank2Node-&
gt;enableMonitoring(QOpcUa::NodeAttribute::
Value, QOpcUaMonitoringParameters(100
));
m_tank2TargetPercentNode-&
gt;enableMonitoring(QOpcUa::NodeAttribute::
Value, QOpcUaMonitoringParameters(100
));
m_tank2ValveStateNode-&
gt;enableMonitoring(QOpcUa::NodeAttribute::
Value, QOpcUaMonitoringParameters(100
));
// Connect the handler for async reading
QObject::
connect(m_machineDesignationNode.data(), &
amp;QOpcUaNode::
attributeRead, this
, &
amp;OpcUaMachineBackend::
machineDesignationRead);
// Request the value attribute of the machine designation node
m_machineDesignationNode-&
gt;readAttributes(QOpcUa::NodeAttribute::
Value);
// Add handlers for write and call results
QObject::
connect(m_tank2TargetPercentNode.data(), &
amp;QOpcUaNode::
attributeWritten, this
, &
amp;OpcUaMachineBackend::
setpointWritten);
QObject::
connect(m_machineNode.data(), &
amp;QOpcUaNode::
methodCallFinished, this
, &
amp;OpcUaMachineBackend::
handleMethodResult);
// Add handlers for enableMonitoring results
QObject::
connect(m_machineStateNode.data(), &
amp;QOpcUaNode::
enableMonitoringFinished, this
, &
amp;OpcUaMachineBackend::
enableMonitoringFinished);
QObject::
connect(m_percentFilledTank1Node.data(), &
amp;QOpcUaNode::
enableMonitoringFinished, this
, &
amp;OpcUaMachineBackend::
enableMonitoringFinished);
QObject::
connect(m_percentFilledTank2Node.data(), &
amp;QOpcUaNode::
enableMonitoringFinished, this
, &
amp;OpcUaMachineBackend::
enableMonitoringFinished);
QObject::
connect(m_tank2TargetPercentNode.data(), &
amp;QOpcUaNode::
enableMonitoringFinished, this
, &
amp;OpcUaMachineBackend::
enableMonitoringFinished);
QObject::
connect(m_tank2ValveStateNode.data(), &
amp;QOpcUaNode::
enableMonitoringFinished, this
, &
amp;OpcUaMachineBackend::
enableMonitoringFinished);
}
if
(state ==
QOpcUaClient::ClientState::
Connecting)
setMessage(QStringLiteral("Connecting"
));
if
(state ==
QOpcUaClient::ClientState::
Disconnected)
setMessage(QString("Disconnected: %1"
).arg(QMetaEnum::
fromType&
lt;QOpcUaClient::
ClientError&
gt;().valueToKey(static_cast
&
lt;int
&
gt;(m_client-&
gt;error()))));
}
void
OpcUaMachineBackend::
machineStateUpdated(QOpcUa::
NodeAttribute attr, const
QVariant &
amp;value)
{
Q_UNUSED(attr);
MachineState newState =
static_cast
&
lt;MachineState&
gt;(value.toUInt());
if
(newState !=
m_machineState) {
m_machineState =
newState;
emit machineStateChanged(m_machineState);
}
}
void
OpcUaMachineBackend::
percentFilledTank1Updated(QOpcUa::
NodeAttribute attr, const
QVariant &
amp;value)
{
Q_UNUSED(attr);
m_percentFilledTank1 =
value.toDouble();
emit percentFilledTank1Changed(m_percentFilledTank1);
}
void
OpcUaMachineBackend::
percentFilledTank2Updated(QOpcUa::
NodeAttribute attr, const
QVariant &
amp;value)
{
Q_UNUSED(attr);
m_percentFilledTank2 =
value.toDouble();
emit percentFilledTank2Changed(m_percentFilledTank2);
}
void
OpcUaMachineBackend::
tank2TargetPercentUpdated(QOpcUa::
NodeAttribute attr, const
QVariant &
amp;value)
{
Q_UNUSED(attr);
m_tank2TargetPercent =
value.toDouble();
emit tank2TargetPercentChanged(m_tank2TargetPercent);
}
void
OpcUaMachineBackend::
tank2ValveStateUpdated(QOpcUa::
NodeAttribute attr, const
QVariant &
amp;value)
{
Q_UNUSED(attr);
m_tank2ValveState =
value.toBool();
emit tank2ValveStateChanged(m_tank2ValveState);
}
void
OpcUaMachineBackend::
machineDesignationRead(QOpcUa::
NodeAttributes attr)
{
if
(attr &
amp; QOpcUa::NodeAttribute::
Value) {
// Make sure the value attribute has been read
if
(m_machineDesignationNode-&
gt;attributeError(QOpcUa::NodeAttribute::
Value) ==
QOpcUa::UaStatusCode::
Good) {
// Make sure there was no error
m_machineDesignation =
m_machineDesignationNode-&
gt;attribute(QOpcUa::NodeAttribute::
Value).toString(); // Get the attribute from the cache
emit machineDesignationChanged(m_machineDesignation);
}
}
}
void
OpcUaMachineBackend::
setpointWritten(QOpcUa::
NodeAttribute attr, QOpcUa::
UaStatusCode status)
{
if
(attr ==
QOpcUa::NodeAttribute::
Value &
amp;&
amp; status ==
QOpcUa::UaStatusCode::
Good)
setMessage("Setpoint successfully set"
);
else
if
(attr ==
QOpcUa::NodeAttribute::
Value &
amp;&
amp; status !=
QOpcUa::UaStatusCode::
Good)
setMessage("Failed to set setpoint"
);
}
void
OpcUaMachineBackend::
handleMethodResult(QString methodNodeId, const
QVariant &
amp;result, QOpcUa::
UaStatusCode statusCode)
{
Q_UNUSED(result);
if
(methodNodeId ==
"ns=2;s=Machine.Start"
) {
if
(statusCode ==
QOpcUa::UaStatusCode::
Good)
setMessage("Pump successfully started"
);
else
setMessage("Unable to start pump"
);
}
else
if
(methodNodeId ==
"ns=2;s=Machine.Stop"
) {
if
(statusCode ==
QOpcUa::UaStatusCode::
Good)
setMessage("Pump successfully stopped"
);
else
setMessage("Unable to stop pump"
);
}
else
if
(methodNodeId ==
"ns=2;s=Machine.FlushTank2"
) {
if
(statusCode ==
QOpcUa::UaStatusCode::
Good)
setMessage("Flushing tank 2 successfully started"
);
else
setMessage("Unable to flush tank 2"
);
}
else
if
(methodNodeId ==
"ns=2;s=Machine.Reset"
) {
if
(statusCode ==
QOpcUa::UaStatusCode::
Good)
setMessage("Simulation successfully reset"
);
else
setMessage("Unable to reset simulation"
);
}
}
void
OpcUaMachineBackend::
enableMonitoringFinished(QOpcUa::
NodeAttribute attr, QOpcUa::
UaStatusCode status)
{
Q_UNUSED(attr);
if
(!
sender())
return
;
if
(status ==
QOpcUa::UaStatusCode::
Good)
qDebug() &
lt;&
lt; "Monitoring successfully enabled for"
&
lt;&
lt; qobject_cast&
lt;QOpcUaNode *&
gt;(sender())-&
gt;nodeId();
else
{
qDebug() &
lt;&
lt; "Failed to enable monitoring for"
&
lt;&
lt; qobject_cast&
lt;QOpcUaNode *&
gt;(sender())-&
gt;nodeId() &
lt;&
lt; ":"
&
lt;&
lt; status;
setMessage("Failed to enable monitoring"
);
}
}
void
OpcUaMachineBackend::
setBackends(const
QStringList &
amp;backends)
{
if
(m_backends !=
backends) {
m_backends =
backends;
emit backendsChanged(m_backends);
}
}
QStringList OpcUaMachineBackend::
backends() const
{
return
m_backends;
}
double
OpcUaMachineBackend::
percentFilledTank1() const
{
return
m_percentFilledTank1;
}
double
OpcUaMachineBackend::
percentFilledTank2() const
{
return
m_percentFilledTank2;
}
OpcUaMachineBackend::
MachineState OpcUaMachineBackend::
machineState() const
{
return
m_machineState;
}
void
OpcUaMachineBackend::
machineWriteTank2TargetPercent(double
value)
{
if
(m_tank2TargetPercentNode)
m_tank2TargetPercentNode-&
gt;writeAttribute(QOpcUa::NodeAttribute::
Value, value);
}
void
OpcUaMachineBackend::
startPump()
{
m_machineNode-&
gt;callMethod("ns=2;s=Machine.Start"
);
}
void
OpcUaMachineBackend::
stopPump()
{
if
(m_machineNode)
m_machineNode-&
gt;callMethod("ns=2;s=Machine.Stop"
);
}
void
OpcUaMachineBackend::
flushTank2()
{
if
(m_machineNode)
m_machineNode-&
gt;callMethod("ns=2;s=Machine.FlushTank2"
);
}
void
OpcUaMachineBackend::
resetSimulation()
{
if
(m_machineNode)
m_machineNode-&
gt;callMethod("ns=2;s=Machine.Reset"
);
}
void
OpcUaMachineBackend::
requestEndpointsFinished(const
QList&
lt;QOpcUaEndpointDescription&
gt; &
amp;endpoints)
{
if
(endpoints.isEmpty()) {
qWarning() &
lt;&
lt; "The server did not return any endpoints"
;
clientStateHandler(QOpcUaClient::ClientState::
Disconnected);
return
;
}
m_client-&
gt;connectToEndpoint(endpoints.at(0
));
}
void
OpcUaMachineBackend::
setMessage(const
QString &
amp;message)
{
if
(message !=
m_message) {
m_message =
message;
emit messageChanged(m_message);
}
}
bool
OpcUaMachineBackend::
successfullyCreated() const
{
return
m_successfullyCreated;
}
QString OpcUaMachineBackend::
message() const
{
return
m_message;
}
QString OpcUaMachineBackend::
machineDesignation() const
{
return
m_machineDesignation;
}
bool
OpcUaMachineBackend::
connected() const
{
return
m_connected;
}
void
OpcUaMachineBackend::
connectToEndpoint(const
QString &
amp;url, qint32 index)
{
if
(m_connected)
return
;
QOpcUaProvider provider;
if
(index &
lt; 0
||
index &
gt;=
m_backends.size())
return
; // Invalid index
if
(!
m_client ||
(m_client &
amp;&
amp; m_client-&
gt;backend() !=
m_backends.at(index))) {
m_client.reset(provider.createClient(m_backends.at(index)));
if
(m_client) {
QObject::
connect(m_client.data(), &
amp;QOpcUaClient::
endpointsRequestFinished, this
, &
amp;OpcUaMachineBackend::
requestEndpointsFinished);
QObject::
connect(m_client.data(), &
amp;QOpcUaClient::
stateChanged, this
, &
amp;OpcUaMachineBackend::
clientStateHandler);
}
}
if
(!
m_client) {
qWarning() &
lt;&
lt; "Could not create client"
;
m_successfullyCreated =
false
;
return
;
}
m_successfullyCreated =
true
;
m_client-&
gt;requestEndpoints(url);
}
void
OpcUaMachineBackend::
disconnectFromEndpoint()
{
if
(m_connected)
m_client-&
gt;disconnectFromEndpoint();
}
bool
OpcUaMachineBackend::
tank2ValveState() const
{
return
m_tank2ValveState;
}
double
OpcUaMachineBackend::
tank2TargetPercent() const
{
return
m_tank2TargetPercent;
}