Results 1 to 11 of 11

Thread: Ice 3.4.0 AMI and Qt 4.5

  1. #1
    mwilson is offline Registered User
    Name: Mark Wilson
    Organization: University of Rochester
    Project: Omega EP laser
    Join Date
    Jul 2005
    Location
    Rochester, NY
    Posts
    100

    Ice 3.4.0 AMI and Qt 4.5

    I have been experimenting with how to notify a Qt app when an Ice AMI call completes or fails. I started with the Ice admin interfaces, Process and PropertiesAdmin. Here is the header for QGetPropertiesForPrefixCB, a callback class that is both a QObject and an Ice reference counted object.

    Code:
    #ifndef _PROPERTY_ADMIN_ASYNC_H_
    #define _PROPERTY_ADMIN_ASYNC_H_
    
    #include <boost/function.hpp>
    
    #include <QObject>
    #include <QSharedPointer>
    
    #include <Ice/Ice.h>
    #include <IceUtil/IceUtil.h>
    
    namespace PropertyAdminAsync
    {
    
    class QGetPropertiesForPrefixCB : public QObject, public IceUtil::Shared
    {
        Q_OBJECT
    
    public:
        typedef IceUtil::Handle<QGetPropertiesForPrefixCB> Ptr;
        typedef boost::function<void(const std::string&)> Func;
    
        static void invokeGetPropertiesForPrefix(
            const QObject* qobject, const Ice::PropertiesAdminPrx& proxy,
                const std::string& prefix);
    
        static Func getInvokeFunc(const QObject* qobject,
            const Ice::PropertiesAdminPrx& proxy);
    
        QGetPropertiesForPrefixCB();
    
        void finished(const Ice::AsyncResultPtr& r);
    
    signals:
        void succeeded(const Ice::PropertyDict& properties);
        void failed(QSharedPointer<Ice::Exception> exception);
    };
    
    }
    
    #endif
    The class provides two Qt signals, succeeded, which provides the return of getPropertiesWithPrefix as its parameter, and failed, which returns a QSharedPointer to any Ice::Exception thrown by the Ice runtime (QSharedPointer is very similar to boost::shared_ptr). The finished method is required by the AMI mechanism, and is invoked by an Ice thread when the AMI call completes or an exception is thrown; it is this method that emits the appropriate Qt signal.

    invokeGetPropertiesForPrefix is a static method that creates a QGetPropertiesForPrefixCB, connects the two signals to the qobject, and calls begin_getPropertiesForPrefix(prefix) on the Ice::PropertiesAdminPrx. The method assumes that the QObject that is passed in has two slots, succeeded(Ice::PropertyDict) failed(QSharedPointer<Ice::Exception>).

    The static getInvokeFunc method creates and returns a boost::function object that can be called with a std::string parameter (the prefix). This function object can be held by the GUI and used to easily invoke the AMI function. For example:

    Code:
        QGetPropertiesForPrefixCB::Func f_ =
            QGetPropertiesForPrefixCB::getInvokeFunc(widget_, proxy_);
    
        ....
        void AdminWidget::handleGetPropertiesAction
        {
            f_("Ice");
        }
    Calling f_("Ice") results in a later callback, either for success or an Ice::Exception. On success, the GUI could fill in a QTableWidget with the names and values of the remote server's Ice properties.

    This class is boiler-plate, and could be easily generated from the slice files, maybe with a "QtAMI" meta-data tag on an interface or individual methods.

    I have attached the definition as PropertiesAdminAsync.cc.gz.

    Questions, comment, and constructive criticisms are appreciated.
    Attached Files Attached Files
    Mark E. Wilson
    Lead Programmer/Analyst
    Omega EP Project
    Laboratory for Laser Energetics (www.lle.rochester.edu)
    University of Rochester
    Rochester, NY 14623

  2. #2
    n2503v is offline Registered User
    Name: Alex Makarenko
    Organization: ACFR, University of Sydney
    Project: Orca
    Join Date
    Jun 2005
    Posts
    134
    Hi Mark,

    Thanks a lot for your post. I like the per-invocation helper instance approach, very elegant.

    One question on the callback mechanism from your helper class back to the widget: what are your thoughts on using Qt signal/slot vs yet another (autogenerated) callback class from which the widget would inherit? I'm sure you've considered both options.

    Thanks again,
    Alex
    Alex Makarenko
    Marathon Robotics
    project: multi-robot systems

  3. #3
    mwilson is offline Registered User
    Name: Mark Wilson
    Organization: University of Rochester
    Project: Omega EP laser
    Join Date
    Jul 2005
    Location
    Rochester, NY
    Posts
    100
    Hi Alex,

    Thanks, glad you like the idea.

    I think the Qt signal/slot mechanism nicely decouples the widget(s) (doesn't have to be widgets - any QObject will work) from the AMI mechanism. In addition, Qt automatically provides cross-thread queuing to get information from the Ice thread that is invoking the AMI callback to the Qt GUI thread.

    I'm not sure what one would gain by generating a class from which to inherit. The implementation of the callback handler is very specific to the type of widget or QObject receiving the callback. Can you give me an example of what you have in mind?

    Mark
    Mark E. Wilson
    Lead Programmer/Analyst
    Omega EP Project
    Laboratory for Laser Energetics (www.lle.rochester.edu)
    University of Rochester
    Rochester, NY 14623

  4. #4
    n2503v is offline Registered User
    Name: Alex Makarenko
    Organization: ACFR, University of Sydney
    Project: Orca
    Join Date
    Jun 2005
    Posts
    134
    Hi Mark,

    Thanks for your explanations. I've been playing with various options of fitting all this stuff into the bigger picture of our code. At the end I've actually settled on signals and slots as the callback mechanism back to the widgets.

    I haven't thought about the thread-safe aspect of signals and slots. This is because I'm using the Ice::Dispatcher facility in conjunction with AMI. It takes care of funneling all Ice calls into the Qt thread.

    The decisive factor for me was the need for some kind of smart pointer mechanism to ensure that the widget still exists when the remote call tries to call back. I've played with using Ice smart pointers throughout but it becomes clumsy when trying not to step on the toes of the Qt's internal garbage collection. So the easiest thing for me is to rely on the Qt's runtime mechanism of ensuring that both emitter and receiver of signals are still in scope.

    Thanks again for your original post and the following discussion.
    Alex Makarenko
    Marathon Robotics
    project: multi-robot systems

  5. #5
    mwilson is offline Registered User
    Name: Mark Wilson
    Organization: University of Rochester
    Project: Omega EP laser
    Join Date
    Jul 2005
    Location
    Rochester, NY
    Posts
    100
    Hi Alex,

    I missed the Ice:ispatcher API. I don't think it was mentioned in the release notes...). I will need to figure out the implications...

    The Qt signal/slot mechanism works across threads via Qt::QueuedConnection connection type; Qt figures out if the sender and receiver are in different threads, and if so it marshalls the signal and queues it up for the GUI thread to pull off and execute (I believe this is new to Qt 4). If a QObject receiver is deleted, this fact is detected and the sender will stop dispatching signals to that object. Another aspect of Qt memory management is that QObjects will delete all children automatically, as long as you have pass a non-NULL parent to those children.
    Mark E. Wilson
    Lead Programmer/Analyst
    Omega EP Project
    Laboratory for Laser Energetics (www.lle.rochester.edu)
    University of Rochester
    Rochester, NY 14623

  6. #6
    n2503v is offline Registered User
    Name: Alex Makarenko
    Organization: ACFR, University of Sydney
    Project: Orca
    Join Date
    Jun 2005
    Posts
    134
    I missed the Ice:Dispatcher API. I don't think it was mentioned in the release notes...). I will need to figure out the implications...
    The nice thing about using Ice:Dispatcher is that it lets you route all incoming calls into the Qt thread as well.

    Here's my implementation of a Qt version of Dispatcher.

    qtdispatcher.h

    Code:
    #ifndef QTDISPATCHER_H
    #define QTDISPATCHER_H
    
    #include <QObject>
    #include <Ice/Dispatcher.h>
    
    class QtDispatcher : public QObject,
                         public Ice::Dispatcher
    {
        Q_OBJECT
    public:
        QtDispatcher( bool debug=false );
    
        // from Ice::Dispatcher
        // This function is called from inside the Ice thread pool.
        // If an instance of QApplication already exists, post a Qt event to itself.
        // The event contains dispatcher call and connection information.
        // It will be recieved and processed by customEvent().
        virtual void dispatch( const Ice::DispatcherCallPtr& d, const Ice::ConnectionPtr& c );
    
    private:
        // from QObject
        // This event is called from inside the Qt main event loop (the Qt "GUI thread")
        // Processes events sent by dispatch().
        virtual void customEvent( QEvent* e );
    
        bool debug_;
    };
    #endif
    qtdispatcher.cpp

    Code:
    #include "qtdispatcher.h"
    #include <QApplication>
    #include <Ice/Connection.h>
    
    #include <iostream>
    using namespace std;
    
    namespace {
    
    const int CUSTOM_EVENT_TYPE = QEvent::User+4021;
    
    // This event is internal to the library, not used by the end user.
    class DispatchEvent : public QEvent
    {
    public:
        DispatchEvent(  const Ice::DispatcherCallPtr& d, const Ice::ConnectionPtr& c ) :
            QEvent( QEvent::Type(CUSTOM_EVENT_TYPE) ),
            dispatcherCall(d),
            connection(c)
        {};
        Ice::DispatcherCallPtr dispatcherCall;
        Ice::ConnectionPtr connection;
    };
    
    }
    
    ////////////////////////////
    
    QtDispatcher::QtDispatcher( bool debug ) :
        debug_(debug)
    {
    }
    
    void
    QtDispatcher::dispatch( const Ice::DispatcherCallPtr& d, const Ice::ConnectionPtr& c )
    {
        if ( debug_ ) {
            if ( c->getInfo()->incoming )
                cout<<"QtDispatcher::"<<__func__<<"(): incoming call"<<endl;
            else
                cout<<"QtDispatcher::"<<__func__<<"(): AMI call"<<endl;
        }
        if ( qApp ) {
            if ( debug_ )
                cout<<"QtDispatcher::dispatch(): dispatching event with qApp->postEvent()"<<endl;
            qApp->postEvent( this, (QEvent*)new DispatchEvent( d, c ) );
        }
        else {
            if ( debug_ )
                cout<<"QtDispatcher::dispatch(): executing remote call directly with d->run()"<<endl;
            d->run(); // Does not throw, blocks until op completes.
        }
    }
    
    void
    QtDispatcher::customEvent( QEvent* event )
    {
        if ( debug_ )
            cout<<"QtDispatcher::"<<__func__<<"()"<<endl;
    
        assert( event->type() == CUSTOM_EVENT_TYPE );
        DispatchEvent* e = dynamic_cast<DispatchEvent*>(event);
        assert( e );
    
        try {
            e->dispatcherCall->run(); // Does not throw, blocks until op completes.
        }
        catch ( const Ice::Exception& e ) {
            cout<<"QtDispatcher::customEvent(): caught ex: "<<e<<endl;
        }
    }
    Alex Makarenko
    Marathon Robotics
    project: multi-robot systems

  7. #7
    n2503v is offline Registered User
    Name: Alex Makarenko
    Organization: ACFR, University of Sydney
    Project: Orca
    Join Date
    Jun 2005
    Posts
    134
    Something to keep in mind when using Ice::Dispatcher: you have to be careful not to mix synchronous and asynchronous calls.

    This thread could be useful:
    checkedCast() through Ice::Dispatcher
    Alex Makarenko
    Marathon Robotics
    project: multi-robot systems

  8. #8
    mwilson is offline Registered User
    Name: Mark Wilson
    Organization: University of Rochester
    Project: Omega EP laser
    Join Date
    Jul 2005
    Location
    Rochester, NY
    Posts
    100
    Alex,

    Could you please post a simple example of how you update a GUI via the dispatcher mechanism?

    Thanks!
    Mark E. Wilson
    Lead Programmer/Analyst
    Omega EP Project
    Laboratory for Laser Energetics (www.lle.rochester.edu)
    University of Rochester
    Rochester, NY 14623

  9. #9
    n2503v is offline Registered User
    Name: Alex Makarenko
    Organization: ACFR, University of Sydney
    Project: Orca
    Join Date
    Jun 2005
    Posts
    134
    Hi Mark,

    There's not much code to change if you are using Ice::Application. All you have to do is initialize Ice runtime with your custom dispatcher and the rest happens magically. I don't have a complete and simple example (our code wraps all of this stuff up in our own classes).

    If you use Ice::Application (MyApplication below derives from it) this is all you have to do (I haven't actually compiled this, sorry):

    Code:
    int main( int argc, char * argv[] )
    {
        MyApplication app;
        Ice::InitializationData initData;
        initData.dispatcher = new QtDispatcher;
        return app.main( argc, argv, initData );
    }
    Code:
    void MyApplication::run()
    {    
        // activate adapter
       
        // Set up Qt
        int c = 0;
        char **v = 0;
        QApplication qapp(c,v);
    
        MainWindow gui;
        gui.show();
    
        qApp->exec();
    }
    There could be a brief period when the adapter is up and accepting calls but the QApplication's event loop is not running yet. The events posted by the QtDispatcher would be lost in this case. That's why the dispatcher checks if qApp exists and executes Ice calls directly if it doesn't. That's it.

    Cheers,
    Alex
    Alex Makarenko
    Marathon Robotics
    project: multi-robot systems

  10. #10
    mwilson is offline Registered User
    Name: Mark Wilson
    Organization: University of Rochester
    Project: Omega EP laser
    Join Date
    Jul 2005
    Location
    Rochester, NY
    Posts
    100
    So, when an AMI completes, the callback object that was given to begin_op eventually makes its way to the QtDispatcher, and the succeeded or failed signals would be emitted. Is this correct?
    Last edited by mwilson; 08-05-2010 at 09:42 AM. Reason: To add an example
    Mark E. Wilson
    Lead Programmer/Analyst
    Omega EP Project
    Laboratory for Laser Energetics (www.lle.rochester.edu)
    University of Rochester
    Rochester, NY 14623

  11. #11
    n2503v is offline Registered User
    Name: Alex Makarenko
    Organization: ACFR, University of Sydney
    Project: Orca
    Join Date
    Jun 2005
    Posts
    134
    Yes.

    The Ice runtime will give QtDispatcher an instance of the Ice::DispatcherCall which encapsulates the state of any Ice call, in this case AMI completion notification.

    This object will be posted in the form a Qt event into the Qt event loop and executed from there.

    It will find the callback method of the callback object specified at the initiation of the AMI call. (I'm not sure exactly how this happens across different threads, but it does work.)

    Then the Qt success/failure signals will be emitted by the callback object.
    Alex Makarenko
    Marathon Robotics
    project: multi-robot systems

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. New Ice 3.4.0 AMI and Qt 4.5 library
    By mwilson in forum Projects
    Replies: 0
    Last Post: 08-12-2010, 03:58 PM
  2. USing ICE from QT app
    By ngambek2003 in forum Help Center
    Replies: 1
    Last Post: 05-03-2010, 04:29 AM
  3. Ice/Ruby/Qt example
    By Andreas Scherer in forum Comments
    Replies: 3
    Last Post: 11-07-2006, 05:00 PM
  4. Ice with Qt 3
    By Andreas Scherer in forum Comments
    Replies: 0
    Last Post: 09-07-2006, 04:47 AM
  5. ICE with Qt ?
    By mlo in forum Help Center
    Replies: 1
    Last Post: 06-22-2003, 09:33 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •