Changeset 148 in pacpusframework for branches


Ignore:
Timestamp:
Jul 31, 2013, 2:42:05 PM (11 years ago)
Author:
Marek Kurdej
Message:

Corrections in PacpusEvent, InputOutputBase, InputOutputInterface.
Fixed: DLL import/export in geodesie.h.

Location:
branches/2.0-beta1
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/2.0-beta1/include/Pacpus/PacpusTools/ShMem.h

    r126 r148  
    3232///
    3333/// Inherits from Win32ShMem on Windows system and from PosixShMem on Unix-like systems.
    34 class PACPUS_TOOLS_API ShMem
     34class /*PACPUS_TOOLS_API*/ ShMem
    3535    : public ShMemType
    3636{
  • branches/2.0-beta1/include/Pacpus/PacpusTools/geodesie.h

    r126 r148  
    128128
    129129////////////////////////////////////////////////////////////////////////
    130 void Geographique_2_Lambert93(const Raf98& raf98,double lambda,double phi,double he,Matrice in,double& E,double& N,double& h,Matrice& out);
    131 void Geographique_2_Lambert93(const Raf98& raf98,double lambda,double phi,double he,double& E,double& N,double& h);
    132 void Lambert93_2_Geographique(const Raf98& raf98,double E,double N,double h,double& lambda,double& phi,double& he);
    133 void Lambert93_2_Geographique(const Raf98& raf98,double E,double N,double h,Matrice in,double& lambda,double& phi,double& he,Matrice& out);
     130PACPUS_TOOLS_API void Geographique_2_Lambert93(const Raf98& raf98,double lambda,double phi,double he,Matrice in,double& E,double& N,double& h,Matrice& out);
     131PACPUS_TOOLS_API void Geographique_2_Lambert93(const Raf98& raf98,double lambda,double phi,double he,double& E,double& N,double& h);
     132PACPUS_TOOLS_API void Lambert93_2_Geographique(const Raf98& raf98,double E,double N,double h,double& lambda,double& phi,double& he);
     133PACPUS_TOOLS_API void Lambert93_2_Geographique(const Raf98& raf98,double E,double N,double h,Matrice in,double& lambda,double& phi,double& he,Matrice& out);
    134134/** Convert from geographique to ECEF.
    135135 * @param[in] longitude Longitude in radian.
     
    137137 * @param[in] he Height in meter.
    138138 */
    139 void Geographique_2_ECEF(double longitude, double latitude, double he, double& x, double& y, double& z);
     139PACPUS_TOOLS_API void Geographique_2_ECEF(double longitude, double latitude, double he, double& x, double& y, double& z);
    140140/** Convert from ECEF two ENU.
    141141 * @param[in] lon0 Longitude of the origin in radian.
     
    143143 * @param[in] he0 Height of the origin in radian.
    144144 */
    145 void ECEF_2_ENU(double x,double y,double z,double& e,double& n,double& u,double lon0,double lat0,double he0);
     145PACPUS_TOOLS_API void ECEF_2_ENU(double x,double y,double z,double& e,double& n,double& u,double lon0,double lat0,double he0);
    146146////////////////////////////////////////////////////////////////////////
    147147
    148148///ALGO0001
    149149/// @todo Rename
    150 double LatitueIsometrique(double latitude,double e);
     150PACPUS_TOOLS_API double LatitueIsometrique(double latitude,double e);
    151151///ALGO0002
    152152/// @todo Rename
    153 double LatitueIsometrique2Lat(double latitude_iso,double e,double epsilon);
     153PACPUS_TOOLS_API double LatitueIsometrique2Lat(double latitude_iso,double e,double epsilon);
    154154
    155155///ALGO0003
    156 void Geo2ProjLambert(
     156PACPUS_TOOLS_API void Geo2ProjLambert(
    157157    double lambda,double phi,
    158158    double n, double c,double e,
     
    160160    double& X,double& Y);
    161161///ALGO0004
    162 void Proj2GeoLambert(
     162PACPUS_TOOLS_API void Proj2GeoLambert(
    163163    double X,double Y,
    164164    double n, double c,double e,
     
    167167    double& lambda,double& phi);
    168168
    169 double ConvMerApp(double longitude);
     169PACPUS_TOOLS_API double ConvMerApp(double longitude);
    170170
    171171/**
     
    209209}
    210210
    211 QMatrix4x4 yprenuToMatrix(QVector3D angle, QVector3D position);
     211PACPUS_TOOLS_API QMatrix4x4 yprenuToMatrix(QVector3D angle, QVector3D position);
    212212
    213213} // namespace Geodesie
  • branches/2.0-beta1/include/Pacpus/kernel/InputOutputBase.h

    r137 r148  
    77
    88#include <QApplication>
     9#include <QList>
     10#include <QString>
     11#include <QStringList>
    912#include <typeinfo>
    10 #include <QList>
    11 #include <QStringList>
    1213
    1314namespace pacpus {
     
    1516class ComponentBase;
    1617
    17 class PACPUSLIB_API AbstractInterface : public QObject
     18class PACPUSLIB_API AbstractInterface
     19    : public QObject
    1820{
    1921     Q_OBJECT
     22
    2023protected:
    21     AbstractInterface(QString name, ComponentBase * component, QObject * parent = 0):_name(name),_component(component),QObject(parent) {}
    22     ~AbstractInterface(){}
     24    AbstractInterface(QString name, ComponentBase * component, QObject * parent = 0)
     25        : m_name(name)
     26        , m_component(component)
     27        , QObject(parent)
     28    {}
     29
     30    ~AbstractInterface()
     31    {}
     32
    2333public:
    2434    QString getSignature();
    25     QString getName() {return _name;}
     35    QString getName()
     36    {
     37        return m_name;
     38    }
     39   
    2640    virtual QString getDataType() = 0;
    27     ComponentBase * getComponent() {return _component;}
    28 
    29     void addConnection(ConnectionBase connection) { _connection.append(connection);}
    30     bool removeConnection(ConnectionBase connection) { return _connection.removeOne(connection);}
    31 
    32     bool hasConnection() { return _connection.size() > 0;}
    33 
     41   
     42    ComponentBase * getComponent()
     43    {
     44        return m_component;
     45    }
     46
     47    void addConnection(ConnectionBase connection)
     48    {
     49        m_connections.append(connection);
     50    }
     51
     52    bool removeConnection(ConnectionBase connection)
     53    {
     54        return m_connections.removeOne(connection);
     55    }
     56
     57    bool hasConnection()
     58    {
     59        return m_connections.size() > 0;
     60    }
     61   
    3462protected:
    35     QString _name;
    36     ComponentBase * _component;
    37     QList<ConnectionBase> _connection;
     63    QList<ConnectionBase> & connections()
     64    {
     65        return m_connections;
     66    }
     67
     68    const QList<ConnectionBase> & connections() const
     69    {
     70        return m_connections;
     71    }
     72
     73    QString name()
     74    {
     75        return m_name;
     76    }
     77
     78    ComponentBase * component()
     79    {
     80        return m_component;
     81    }
     82
     83    const ComponentBase * component() const
     84    {
     85        return m_component;
     86    }
     87   
     88private:
     89    QString m_name;
     90    ComponentBase * m_component;
     91    QList<ConnectionBase> m_connections;
    3892};
    3993
    40 class PACPUSLIB_API InputInterfaceBase : public AbstractInterface
     94class PACPUSLIB_API InputInterfaceBase
     95    : public AbstractInterface
    4196{
    4297    Q_OBJECT
    4398protected:
    44     InputInterfaceBase(QString name, ComponentBase * component, QObject * parent = 0):AbstractInterface(name,component,parent) {}
     99    InputInterfaceBase(QString name, ComponentBase * component, QObject * parent = 0)
     100        : AbstractInterface(name, component, parent)
     101    {}
    45102
    46103public:
    47104    //InputInterfaceBase(QString name, ComponentBase * component,int a, QObject * parent = 0):AbstractInterface(name,component,parent) {} // TODO add ctor with function pointer
    48105
    49             enum ReadingMode {
     106    enum ReadingMode {
    50107        NeverSkip,
    51108        TimeBounded,
     
    53110    };
    54111
    55     virtual ~InputInterfaceBase(){}
    56 
    57     virtual void customEvent(QEvent* e) {
    58 
     112    virtual ~InputInterfaceBase()
     113    {}
     114
     115    virtual void customEvent(QEvent* e)
     116    {
    59117        //if(event->type())
    60             //TODO get event Type anf call callback function
    61 
    62             PacpusEvent * event = dynamic_cast<PacpusEvent *>(e);
    63     QByteArray buf;
    64     QDataStream out(&buf,QIODevice::WriteOnly);
    65     event->streamOut(out);
    66     // Callback QByteArray
    67     }
    68 
    69     void setReadingMode(ReadingMode mode) { readingMode_ = mode;}
    70     virtual PacpusEvent* getEventTemplate() {return new PacpusEvent(GENERIC_EVENT);} // TODO check ??
    71 protected:
    72             ReadingMode readingMode_;
     118        //TODO get event Type anf call callback function
     119
     120        PacpusEvent * event = dynamic_cast<PacpusEvent *>(e);
     121        QByteArray buf;
     122        QDataStream out(&buf, QIODevice::WriteOnly);
     123        event->streamOut(out);
     124        // Callback QByteArray
     125    }
     126
     127    ReadingMode & readingMode()
     128    {
     129        return m_readingMode;
     130    }
     131
     132    const ReadingMode & readingMode() const
     133    {
     134        return m_readingMode;
     135    }
     136
     137    void setReadingMode(ReadingMode mode)
     138    {
     139        m_readingMode = mode;
     140    }
     141
     142    virtual PacpusEvent* getEventTemplate()
     143    {
     144        // TODO: check
     145        return new PacpusEvent(GENERIC_EVENT);
     146    }
     147   
    73148private:
    74         // metode(QByteArray)
    75 
    76         //QQueue jobQueue_;
     149    ReadingMode m_readingMode;
     150
     151    // metode(QByteArray)
     152    //QQueue jobQueue_;
    77153};
    78154
    79 class PACPUSLIB_API OutputInterfaceBase: public AbstractInterface
     155class PACPUSLIB_API OutputInterfaceBase
     156    : public AbstractInterface
    80157{
    81158    Q_OBJECT
    82159
    83160public:
    84     OutputInterfaceBase(QString name, ComponentBase * component, QObject * parent = 0):AbstractInterface(name,component,parent) {}
    85 
    86     virtual ~OutputInterfaceBase(){}
    87 
    88     QStringList getInputConnectedList() {
     161    OutputInterfaceBase(QString name, ComponentBase * component, QObject * parent = 0)
     162        : AbstractInterface(name, component, parent)
     163    {}
     164
     165    virtual ~OutputInterfaceBase()
     166    {}
     167
     168    QStringList getInputConnectedList()
     169    {
    89170        QStringList list;
    90         for(QList<ConnectionBase>::iterator it = _connection.begin(); it!=_connection.end(); ++it)
     171        for(QList<ConnectionBase>::iterator it = connections().begin(); it != connections().end(); ++it) {
    91172            list.append(it->getInterface()->getName());
     173        }
    92174        return list;
    93175    }
    94176
    95     void send(/*const*/ QByteArray & data) {
     177    void send(/*const*/ QByteArray & data)
     178    {
    96179        // TODO check at least one Typed connection
    97180
    98         for(QList<ConnectionBase>::iterator it = _connection.begin(); it!=_connection.end(); ++it){
     181        for(QList<ConnectionBase>::iterator it = connections().begin(); it!=connections().end(); ++it){
    99182                QDataStream in(&data,QIODevice::ReadOnly);
    100                 PacpusEvent* event = dynamic_cast<InputInterfaceBase*>(_connection.at(0).getInterface())->getEventTemplate();
     183                PacpusEvent* event = dynamic_cast<InputInterfaceBase*>(connections().at(0).getInterface())->getEventTemplate();
    101184                event->streamIn(in);
    102185                QApplication::postEvent(it->getInterface(),event,it->getPriority());
    103186        }
    104 
    105 
    106187    }
    107188
     
    111192{
    112193    if(out->getDataType() == in->getDataType() || out->getDataType() == QString(typeid(QByteArray).name()) || in->getDataType() == QString(typeid(QByteArray).name())) {
    113 
    114194        // Add connection
    115195        out->addConnection(ConnectionBase(in,priority));  // TODO make connect function
     
    119199        return true;
    120200    } else {
    121    //LOG_WARN("connecting " << out->getSignature() << ":" << out->getDataType() << " to " << in->getSignature() << ":" << in->getDataType() << " failled : DataType incompatible " <<  QString(typeid(QByteArray).name()));
    122    return false;
     201        //LOG_WARN("connecting " << out->getSignature() << ":" << out->getDataType() << " to " << in->getSignature() << ":" << in->getDataType() << " failled : DataType incompatible " <<  QString(typeid(QByteArray).name()));
     202        return false;
    123203    }
    124204}
  • branches/2.0-beta1/include/Pacpus/kernel/InputOutputInterface.h

    r138 r148  
    2121
    2222template <class T, class C>
    23 class InputInterface : public InputInterfaceBase
     23class InputInterface
     24    : public InputInterfaceBase
    2425{
    2526public:
    26     InputInterface(QString name, C * component, void (C::*m)(const T&)):InputInterfaceBase(name,component,component), method(m) {}
    27     ~InputInterface() {}
    28     size_t getDataSize() {return sizeof(T);}
    29     QString getDataType() {return QString(typeid(T).name());}
     27    InputInterface(QString name, C * component, void (C::*m)(const T&))
     28        : InputInterfaceBase(name,component,component)
     29        , method(m)
     30    {}
    3031
    31 protected:
     32    ~InputInterface()
     33    {}
    3234
    33 public:
     35    size_t getDataSize()
     36    {
     37        return sizeof(T);
     38    }
    3439
    35     PacpusEvent* getEventTemplate() {return new PacpusTypedEvent<T>(TYPED_EVENT); }
     40    QString getDataType()
     41    {
     42        return QString(typeid(T).name());
     43    }
    3644
    37     void customEvent(QEvent* event) {
    38 // TODO check compoennt state started
     45    PacpusEvent* getEventTemplate()
     46    {
     47        return new PacpusTypedEvent<T>(TYPED_EVENT);
     48    }
     49
     50    void customEvent(QEvent* event)
     51    {
     52        // TODO check component state started
    3953        switch (event->type()) {
    40 
    41         // from Component to Component (T->T)
    42         case TYPED_EVENT: {
     54        case TYPED_EVENT:
     55            {
     56            // cast from Component to Component (T->T)
    4357            PacpusTypedEvent<T> * typedEvent = dynamic_cast<PacpusTypedEvent<T> *> (event);
    4458
     
    4761            //if(_component) get state
    4862
    49             if(typedEvent->tr_ < 500 && readingMode_ == TimeBounded) {
     63            if (typedEvent->timerange() < 500 && readingMode() == TimeBounded) {
    5064                //LOG_WARN("Incorrect TimeRange (0), switch to NeverSkip");
    5165                qDebug() << "Incorrect TimeRange (0), switch to NeverSkip";
    52                 readingMode_ = NeverSkip;}
     66                readingMode() = NeverSkip;}
    5367
    54             switch (readingMode_){
     68            switch (readingMode()) {
    5569            case TimeBounded:
    5670                //qDebug() << "Input " << this->getSignature().leftJustified(20) << QString("Time bournded").leftJustified(15) << road_time()- typedEvent->t_ << "\t" << typedEvent->tr_;
    5771
    58                 if(road_time() - typedEvent->t_> typedEvent->tr_)
    59                     {qDebug() << "Data skip " << this->getSignature();  break;}
     72                if (road_time() - typedEvent->time() > typedEvent->timerange()) {
     73                    qDebug() << "Data skip " << this->getSignature();
     74                    break;
     75                }
    6076
    61                 (dynamic_cast<C*>(_component)->*method)(typedEvent->data_);
     77                (dynamic_cast<C*>(component())->*method)(typedEvent->data());
    6278                break;
    6379
     
    6581                //qDebug() << "Input " << this->getSignature().leftJustified(20) << QString("GetLast").leftJustified(15) << road_time() - typedEvent->t_ << "\t" << typedEvent->tr_;
    6682
    67                 (dynamic_cast<C*>(_component)->*method)(typedEvent->data_);
     83                (dynamic_cast<C*>(component())->*method)(typedEvent->data());
    6884                QCoreApplication::removePostedEvents(this,TYPED_EVENT); // delete all remining events
    6985                break;
     
    7389
    7490            default:
    75                 (dynamic_cast<C*>(_component)->*method)(typedEvent->data_);
     91                (dynamic_cast<C*>(component())->*method)(typedEvent->data());
    7692            }
    7793            break;
     
    136152        //QSharedPointer<T> sharedPointer = new T(data);
    137153
    138         for(QList<ConnectionBase>::iterator it = _connection.begin(); it!=_connection.end(); ++it){
     154        for(QList<ConnectionBase>::iterator it = connections().begin(); it != connections().end(); ++it){
    139155            QApplication::postEvent(it->getInterface(),new PacpusTypedEvent<T>(TYPED_EVENT,data,t,tr),it->getPriority()); // Event is delete by the event loop handler
    140156            //qDebug() << "sender " << it->getInterface()->getSignature() <<  " thread " << QThread::currentThread() << " Data & " << &data << " ";
     
    166182    }
    167183*/
    168     size_t getDataSize() {return sizeof(T);}
    169     QString getDataType() {return QString(typeid(T).name());}
     184    size_t getDataSize()
     185    {
     186        return sizeof(T);
     187    }
     188
     189    QString getDataType()
     190    {
     191        return QString(typeid(T).name());
     192    }
    170193};
    171194
  • branches/2.0-beta1/include/Pacpus/kernel/PacpusEvent.h

    r146 r148  
    22#define PACPUS_EVENT_H
    33
    4 #include <QEvent>
    5 #include "Pacpus/kernel/road_time.h"
     4#include <algorithm>
     5#include <Pacpus/kernel/road_time.h>
    66#include <Pacpus/kernel/pacpus.h>
    77#include <QDataStream>
     8#include <QEvent>
    89
    910namespace pacpus {
     
    1112// registerEventType // TODO
    1213
    13 class GenericData {
    14 public:
    15     GenericData(char* data, size_t size) {
    16         data_ = (char*)malloc(size);
    17         memcpy(data_,data,size);
    18         _size = size;}
    19 
    20     ~GenericData() {free(data_);}
    21 
    22     char * data() { return data_;}
    23     size_t size() {return _size;}
    24 
    25 private:
    26 char* data_;
    27 size_t _size;
    28 };
    29 
    30 enum PacpusEventType  {
     14// FIXME: move to another header file
     15enum PacpusEventType {
    3116    TYPED_EVENT = 1000,
    3217    GENERIC_EVENT = 2000,
     
    4429    PacpusEvent(PacpusEventType type, road_time_t t = road_time(), road_timerange_t tr = 0)
    4530        : QEvent(QEvent::Type(type))
    46         , t_(t)
    47         , tr_(tr)
     31        , m_time(t)
     32        , m_timerange(tr)
    4833    {}
     34
    4935    virtual ~PacpusEvent()
    5036    {}
    5137
    52     // NOTE virtual pure ??
     38    // TODO: should we make it virtual pure ???
    5339    virtual QDataStream& streamOut(QDataStream& out)
    5440    {
    5541        return out;
    56     }
     42    }
     43
     44    // TODO: should we make it virtual pure ???
    5745    virtual QDataStream& streamIn(QDataStream& in)
    5846    {
     
    6048    }
    6149
    62 protected: // TODO make protected
    63     road_time_t t_;
    64     road_timerange_t tr_;
     50    road_time_t & time()
     51    {
     52        return m_time;
     53    }
     54
     55    const road_time_t & time() const
     56    {
     57        return m_time;
     58    }
     59
     60    road_timerange_t & timerange()
     61    {
     62        return m_timerange;
     63    }
     64
     65    const road_timerange_t & timerange() const
     66    {
     67        return m_timerange;
     68    }
     69protected:
     70    road_time_t m_time;
     71    road_timerange_t m_timerange;
     72};
     73
     74template <typename T>
     75class /*PACPUSLIB_API*/ TypedData
     76{
     77public:
     78    TypedData(const T& data)
     79    : m_data(data)
     80    {
     81    }
     82
     83    ~TypedData()
     84    {
     85    }
     86
     87    T & data()
     88    {
     89        return m_data;
     90    }
     91
     92    const T & data() const
     93    {
     94        return m_data;
     95    }
     96
     97    size_t size() const
     98    {
     99        return sizeof(T);
     100    }
     101
     102private:
     103    T m_data;
    65104};
    66105
    67106template<class T>
    68 class PacpusTypedEvent : public PacpusEvent
     107class /*PACPUSLIB_API*/ PacpusTypedEvent
     108    : public PacpusEvent
     109    , public TypedData<T>
    69110{
    70111public:
    71     PacpusTypedEvent(PacpusEventType type, T data = T(), road_time_t t = road_time(), road_timerange_t tr = 0 ):PacpusEvent(type, t, tr),data_(data) {}
    72     ~PacpusTypedEvent() {}
     112    // FIXME: why we need `data = T()` ???
     113    PacpusTypedEvent(PacpusEventType type, const T & data = T(), road_time_t t = road_time(), road_timerange_t tr = 0 )
     114        : PacpusEvent(type, t, tr)
     115        , TypedData(data)
     116    {}
    73117
    74     QDataStream& streamOut(QDataStream& out) {return out << (quint64)t_ << tr_ /*<<data_*/;} // FIXME Stream Data errors
    75     QDataStream& streamIn(QDataStream& in) {return in >> (quint64&)t_ >> tr_ /*>> data_*/;}
     118    ~PacpusTypedEvent()
     119    {}
    76120
    77 protected:
    78     T data_;
     121    QDataStream& streamOut(QDataStream& out)
     122    {
     123        // FIXME Stream Data errors
     124        return out << (quint64) time() << timerange() /*<< m_data*/;
     125    }
     126
     127    QDataStream& streamIn(QDataStream& in)
     128    {
     129        return in >> (quint64&) time() >> timerange() /*>> m_data*/;
     130    }
    79131};
    80132
    81 class PACPUSLIB_API PacpusGenericEvent : public PacpusEvent
     133class PACPUSLIB_API GenericData
    82134{
    83135public:
    84     PacpusGenericEvent(PacpusEventType type, char* data, size_t size)
    85         : PacpusEvent(type)
     136    GenericData(const char* data, size_t size)
     137        : m_data(NULL)
     138        , m_dataSize(size)
    86139    {
    87         data_ = new char[size];
    88         memcpy(data_,data,size);
    89         _size = size;
     140        m_data = new char[m_dataSize];
     141        ::std::copy(data, data + m_dataSize, m_data);
     142    }
    90143
     144    ~GenericData()
     145    {
     146        delete[] m_data;
     147    }
     148
     149    char * data()
     150    {
     151        return m_data;
    91152    }
    92153   
     154    const char * data() const
     155    {
     156        return m_data;
     157    }
     158
     159    size_t size() const
     160    {
     161        return m_dataSize;
     162    }
     163
     164private:
     165    char* m_data;
     166    size_t m_dataSize;
     167};
     168
     169class PACPUSLIB_API PacpusGenericEvent
     170    : public PacpusEvent
     171    , public GenericData
     172{
     173public:
     174    PacpusGenericEvent(PacpusEventType type, const char* data, size_t size)
     175        : PacpusEvent(type)
     176        , GenericData(data, size)
     177    {
     178    }
     179
    93180    virtual ~PacpusGenericEvent()
    94181    {
    95         delete[] data_;
    96182    }
    97 
    98     char* data_;
    99     size_t _size;
    100183};
    101 
    102184
    103185} // namespace pacpus
    104186
    105 inline QDataStream& operator << (QDataStream& out, pacpus::PacpusEvent& ev) {/*return ev.streamOut(out);*/ return out;}
    106 inline QDataStream& operator >> (QDataStream& in, pacpus::PacpusEvent& ev) {/*return ev.streamIn(in);*/ return in;}
     187PACPUSLIB_API inline QDataStream& operator<< (QDataStream& out, pacpus::PacpusEvent& ev)
     188{
     189    /*return ev.streamOut(out);*/
     190    return out;
     191}
     192
     193PACPUSLIB_API inline QDataStream& operator>> (QDataStream& in, pacpus::PacpusEvent& ev)
     194{
     195    /*return ev.streamIn(in);*/
     196    return in;
     197}
    107198
    108199#endif // PACPUS_EVENT_H
  • branches/2.0-beta1/src/PacpusLib/InputOutputBase.cpp

    r137 r148  
    99QString AbstractInterface::getSignature()
    1010{
    11     return _component->getName()+'.'+_name;
     11    return component()->getName() + '.' + name();
    1212}
    13 
    14 
    15 
    16 
Note: See TracChangeset for help on using the changeset viewer.