Qt OPC UA 查看器范例
					 
					
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the Qt OPC UA 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 "treeitem.h" 
#include "opcuamodel.h" 
#include <QOpcUaArgument> 
#include <QOpcUaAxisInformation> 
#include <QOpcUaClient> 
#include <QOpcUaComplexNumber> 
#include <QOpcUaDoubleComplexNumber> 
#include <QOpcUaEUInformation> 
#include <QOpcUaExtensionObject> 
#include <QOpcUaLocalizedText> 
#include <QOpcUaQualifiedName> 
#include <QOpcUaRange> 
#include <QOpcUaXValue> 
#include <QMetaEnum> 
#include <QPixmap> 
const  int  numberOfDisplayColumns =  7 ; // NodeId, Value, NodeClass, DataType, BrowseName, DisplayName, Description 
TreeItem:: TreeItem(OpcUaModel * model) : QObject ,  mModel(model)
{
}
TreeItem:: TreeItem(QOpcUaNode * node,  OpcUaModel * model,  TreeItem * parent) : QObject ,  mOpcNode(node)
  ,  mModel(model)
  ,  mParentItem(parent)
{
    connect(mOpcNode. get(),  & QOpcUaNode :: attributeRead,  this ,  & TreeItem:: handleAttributes);
    connect(mOpcNode. get(),  & QOpcUaNode :: attributeUpdated,  this ,  & TreeItem:: handleAttributes);
    connect(mOpcNode. get(),  & QOpcUaNode :: browseFinished,  this ,  & TreeItem:: browseFinished);
    if  (! mOpcNode- > readAttributes( QOpcUa :: NodeAttribute:: Value
                            |  QOpcUa :: NodeAttribute:: NodeClass
                            |  QOpcUa :: NodeAttribute:: Description
                            |  QOpcUa :: NodeAttribute:: DataType
                            |  QOpcUa :: NodeAttribute:: BrowseName
                            |  QOpcUa :: NodeAttribute:: DisplayName
                            ))
        qWarning () < <  "Reading attributes"  < <  mOpcNode- > nodeId() < <  "failed" ;
}
TreeItem:: TreeItem(QOpcUaNode * node,  OpcUaModel * model,  const  QOpcUaReferenceDescription & browsingData,  TreeItem * parent) : TreeItem(node,  model,  parent)
{
    mNodeBrowseName =  browsingData. browseName(). name();
    mNodeClass =  browsingData. nodeClass();
    mNodeId =  browsingData. targetNodeId(). nodeId();
    mNodeDisplayName =  browsingData. displayName(). text();
}
TreeItem:: ~ TreeItem()
{
    qDeleteAll (mChildItems);
}
TreeItem * TreeItem:: child(int  row)
{
    if  (row > =  mChildItems. size())
        qCritical () < <  "TreeItem in row"  < <  row < <  "does not exist." ;
    return  mChildItems[ row] ;
}
int  TreeItem:: childIndex(const  TreeItem * child) const 
{
    return  mChildItems. indexOf(const_cast < TreeItem * > (child));
}
int  TreeItem:: childCount()
{
    startBrowsing();
    return  mChildItems. size();
}
int  TreeItem:: columnCount() const 
{
    return  numberOfDisplayColumns;
}
QVariant :: data(int  column)
{
    if  (column = =  0 )
        return  mNodeBrowseName;
    if  (column = =  1 ) {
        if  (! mAttributesReady)
            return  tr("Loading ..." );
        const  auto  type =  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: DataType). toString();
        const  auto  value =  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: Value);
        return  variantToString(value,  type);
    }
    if  (column = =  2 ) {
        QMetaEnum =  QMetaEnum :: fromType< QOpcUa :: NodeClass> ();
        QString =  metaEnum. valueToKey(int (mNodeClass));
        return  name +  " ("  +  QString :: number(int (mNodeClass)) +  ')' ;
    }
    if  (column = =  3 ) {
        if  (! mAttributesReady)
            return  tr("Loading ..." );
        const  QString =  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: DataType). toString();
        auto  enumEntry =  QOpcUa :: namespace0IdFromNodeId(typeId);
        if  (enumEntry = =  QOpcUa :: NodeIds:: Namespace0:: Unknown)
            return  typeId;
        return  QOpcUa :: namespace0IdName(enumEntry) +  " ("  +  typeId +  ")" ;
    }
    if  (column = =  4 )
        return  mNodeId;
    if  (column = =  5 )
        return  mNodeDisplayName;
    if  (column = =  6 ) {
        return  mAttributesReady
            ?  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: Description). value< QOpcUaLocalizedText > (). text()
            : tr("Loading ..." );
    }
    return  QVariant int  TreeItem:: row() const 
{
    if  (! mParentItem)
        return  0 ;
    return  mParentItem- > childIndex(this );
}
TreeItem * TreeItem:: parentItem()
{
    return  mParentItem;
}
void  TreeItem:: appendChild(TreeItem * child)
{
    if  (! child)
        return ;
    if  (! hasChildNodeItem(child- > mNodeId)) {
        mChildItems. append(child);
        mChildNodeIds. insert(child- > mNodeId);
    } else  {
        child- > deleteLater();
    }
}
static  QPixmap  createPixmap(const  QColor  & c)
{
    QPixmap  p(10 , 10 );
    p. fill(c);
    return  p;
}
QPixmap  TreeItem:: icon(int  column) const 
{
    if  (column ! =  0  | |  ! mOpcNode)
        return  QPixmap ();
    static  const  QPixmap  objectPixmap =  createPixmap(Qt :: darkGreen);
    static  const  QPixmap  variablePixmap =  createPixmap(Qt :: darkBlue);
    static  const  QPixmap  methodPixmap =  createPixmap(Qt :: darkRed);
    static  const  QPixmap  defaultPixmap =  createPixmap(Qt :: gray);
    switch  (mNodeClass) {
    case  QOpcUa :: NodeClass:: Object:
        return  objectPixmap;
    case  QOpcUa :: NodeClass:: Variable:
        return  variablePixmap;
    case  QOpcUa :: NodeClass:: Method:
        return  methodPixmap;
    default :
        break ;
    }
    return  defaultPixmap;
}
bool TreeItem:: hasChildNodeItem(const  QString & nodeId) const 
{
    return  mChildNodeIds. contains(nodeId);
}
void  TreeItem:: setMonitoringEnabled(bool active)
{
    if  (! supportsMonitoring())
        return ;
    if  (active) {
        mOpcNode- > enableMonitoring(QOpcUa :: NodeAttribute:: Value,  QOpcUaMonitoringParameters 500 ));
    } else  {
        mOpcNode- > disableMonitoring(QOpcUa :: NodeAttribute:: Value);
    }
}
bool TreeItem:: monitoringEnabled() const 
{
    QOpcUaMonitoringParameters =  mOpcNode. get()- > monitoringStatus(QOpcUa :: NodeAttribute:: Value);
    return   monitoring. statusCode() = =  QOpcUa :: UaStatusCode:: Good & & 
            monitoring. monitoringMode() = =  QOpcUaMonitoringParameters :: MonitoringMode:: Reporting;
}
bool TreeItem:: supportsMonitoring() const 
{
    return  mNodeClass = =  QOpcUa :: NodeClass:: Variable;
}
void  TreeItem:: startBrowsing()
{
    if  (mBrowseStarted)
        return ;
    if  (! mOpcNode- > browseChildren())
        qWarning () < <  "Browsing node"  < <  mOpcNode- > nodeId() < <  "failed" ;
    else 
        mBrowseStarted =  true ;
}
void  TreeItem:: handleAttributes(QOpcUa :: NodeAttributes attr)
{
    if  (attr &  QOpcUa :: NodeAttribute:: NodeClass)
        mNodeClass =  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: NodeClass). value< QOpcUa :: NodeClass> ();
    if  (attr &  QOpcUa :: NodeAttribute:: BrowseName)
        mNodeBrowseName =  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: BrowseName). value< QOpcUaQualifiedName > (). name();
    if  (attr &  QOpcUa :: NodeAttribute:: DisplayName)
        mNodeDisplayName =  mOpcNode- > attribute(QOpcUa :: NodeAttribute:: DisplayName). value< QOpcUaLocalizedText > (). text();
    mAttributesReady =  true ;
    emit  mModel- > dataChanged(mModel- > createIndex(row(),  0 ,  this ),  mModel- > createIndex(row(),  numberOfDisplayColumns -  1 ,  this ));
}
void  TreeItem:: browseFinished(const  QList < QOpcUaReferenceDescription >  & children,  QOpcUa :: UaStatusCode statusCode)
{
    if  (statusCode ! =  QOpcUa :: Good) {
        qWarning () < <  "Browsing node"  < <  mOpcNode- > nodeId() < <  "finally failed:"  < <  statusCode;
        return ;
    }
    auto  index =  mModel- > createIndex(row(),  0 ,  this );
    for  (const  auto  & item : children) {
        if  (hasChildNodeItem(item. targetNodeId(). nodeId()))
            continue ;
        auto  node =  mModel- > opcUaClient()- > node(item. targetNodeId());
        if  (! node) {
            qWarning () < <  "Failed to instantiate node:"  < <  item. targetNodeId(). nodeId();
            continue ;
        }
        mModel- > beginInsertRows(index,  mChildItems. size(),  mChildItems. size() +  1 );
        appendChild(new  TreeItem(node,  mModel,  item,  this ));
        mModel- > endInsertRows();
    }
    emit  mModel- > dataChanged(mModel- > createIndex(row(),  0 ,  this ),  mModel- > createIndex(row(),  numberOfDisplayColumns -  1 ,  this ));
}
QString :: variantToString(const  QVariant & value,  const  QString & typeNodeId) const 
{
    if  (value. metaType(). id() = =  QMetaType :: QVariantList const  auto  list =  value. toList();
        QString for  (int  i =  0 ,  size =  list. size(); i <  size; + + i) {
            if  (i)
                concat. append(QLatin1Char('\n' ));
            concat. append(variantToString(list. at(i),  typeNodeId));
        }
        return  concat;
    }
    if  (typeNodeId = =  QLatin1String("ns=0;i=19" )) { // StatusCode 
        const  char  * name =  QMetaEnum :: fromType< QOpcUa :: UaStatusCode> (). valueToKey(value. toInt());
        return  name ?  QLatin1String(name) : QLatin1String("Unknown StatusCode" );
    }
    if  (typeNodeId = =  QLatin1String("ns=0;i=2" )) // Char 
        return  QString :: number(value. toInt());
    if  (typeNodeId = =  QLatin1String("ns=0;i=3" )) // SChar 
        return  QString :: number(value. toUInt());
    if  (typeNodeId = =  QLatin1String("ns=0;i=4" )) // Int16 
        return  QString :: number(value. toInt());
    if  (typeNodeId = =  QLatin1String("ns=0;i=5" )) // UInt16 
        return  QString :: number(value. toUInt());
    if  (value. metaType(). id() = =  QMetaType :: QByteArray return  QLatin1String("0x" ) +  value. toByteArray(). toHex();
    if  (value. metaType(). id() = =  QMetaType :: QDateTime return  value. toDateTime(). toString(Qt :: ISODate);
    if  (value. canConvert< QOpcUaQualifiedName > ()) {
        const  auto  name =  value. value< QOpcUaQualifiedName > ();
        return  QStringLiteral "[NamespaceIndex: %1, Name: \"%2\"]" ). arg(name. namespaceIndex()). arg(name. name());
    }
    if  (value. canConvert< QOpcUaLocalizedText > ()) {
        const  auto  text =  value. value< QOpcUaLocalizedText > ();
        return  localizedTextToString(text);
    }
    if  (value. canConvert< QOpcUaRange > ()) {
        const  auto  range =  value. value< QOpcUaRange > ();
        return  rangeToString(range);
    }
    if  (value. canConvert< QOpcUaComplexNumber > ()) {
        const  auto  complex =  value. value< QOpcUaComplexNumber > ();
        return  QStringLiteral "[Real: %1, Imaginary: %2]" ). arg(complex. real()). arg(complex. imaginary());
    }
    if  (value. canConvert< QOpcUaDoubleComplexNumber > ()) {
        const  auto  complex =  value. value< QOpcUaDoubleComplexNumber > ();
        return  QStringLiteral "[Real: %1, Imaginary: %2]" ). arg(complex. real()). arg(complex. imaginary());
    }
    if  (value. canConvert< QOpcUaXValue > ()) {
        const  auto  xv =  value. value< QOpcUaXValue > ();
        return  QStringLiteral "[X: %1, Value: %2]" ). arg(xv. x()). arg(xv. value());
    }
    if  (value. canConvert< QOpcUaEUInformation > ()) {
        const  auto  info =  value. value< QOpcUaEUInformation > ();
        return  euInformationToString(info);
    }
    if  (value. canConvert< QOpcUaAxisInformation > ()) {
        const  auto  info =  value. value< QOpcUaAxisInformation > ();
        return  QStringLiteral "[EUInformation: %1, EURange: %2, Title: %3 , AxisScaleType: %4, AxisSteps: %5]" ). arg(
                    euInformationToString(info. engineeringUnits())). arg(rangeToString(info. eURange())). arg(localizedTextToString(info. title())). arg(
                        info. axisScaleType() = =  QOpcUa :: AxisScale:: Linear ?  "Linear"  : (info. axisScaleType() = =  QOpcUa :: AxisScale:: Ln) ?  "Ln"  : "Log" ). arg(
                        numberArrayToString(info. axisSteps()));
    }
    if  (value. canConvert< QOpcUaExpandedNodeId > ()) {
        const  auto  id =  value. value< QOpcUaExpandedNodeId > ();
        return  QStringLiteral "[NodeId: \"%1\", ServerIndex: \"%2\", NamespaceUri: \"%3\"]" ). arg(
                    id. nodeId()). arg(id. serverIndex()). arg(id. namespaceUri());
    }
    if  (value. canConvert< QOpcUaArgument > ()) {
        const  auto  a =  value. value< QOpcUaArgument > ();
        return  QStringLiteral "[Name: \"%1\", DataType: \"%2\", ValueRank: \"%3\", ArrayDimensions: %4, Description: %5]" ). arg(
                    a. name(),  a. dataTypeId()). arg(a. valueRank()). arg(numberArrayToString(a. arrayDimensions()), 
                    localizedTextToString(a. description()));
    }
    if  (value. canConvert< QOpcUaExtensionObject > ()) {
        const  auto  obj =  value. value< QOpcUaExtensionObject > ();
        return  QStringLiteral "[TypeId: \"%1\", Encoding: %2, Body: 0x%3]" ). arg(obj. encodingTypeId(), 
                    obj. encoding() = =  QOpcUaExtensionObject :: Encoding:: NoBody ? 
                        "NoBody"  : (obj. encoding() = =  QOpcUaExtensionObject :: Encoding:: ByteString ? 
                            "ByteString"  : "XML" )). arg(obj. encodedBody(). isEmpty() ?  "0"  : QString . encodedBody(). toHex()));
    }
    if  (value. canConvert< QString > ())
        return  value. toString();
    return  QString QString :: localizedTextToString(const  QOpcUaLocalizedText & text) const 
{
    return  QStringLiteral "[Locale: \"%1\", Text: \"%2\"]" ). arg(text. locale()). arg(text. text());
}
QString :: rangeToString(const  QOpcUaRange & range) const 
{
    return  QStringLiteral "[Low: %1, High: %2]" ). arg(range. low()). arg(range. high());
}
QString :: euInformationToString(const  QOpcUaEUInformation & info) const 
{
    return  QStringLiteral "[UnitId: %1, NamespaceUri: \"%2\", DisplayName: %3, Description: %4]" ). arg(info. unitId()). arg(
                info. namespaceUri()). arg(localizedTextToString(info. displayName())). arg(localizedTextToString(info. description()));
}