Menu

LabVIEW-example for "The Asynchronous Client/Server Pattern"

2022-03-28
2022-05-29
  • Niels Göran Blume

    Hello everyone,

    I'm currently trying to setup a ZMQ-based system in LabVIEW and came across the "The Asynchronous Client/Server Pattern" pattern (https://zguide.zeromq.org/docs/chapter3/#The-Asynchronous-Client-Server-Pattern).
    It seems logical to me, but implementation-wise, I'm having a hard time, wrapping my head around it.
    Does anyone have some kind of example with this or a similar pattern?

    The system:
    cRIO which acts as server and gets commands from multiple clients, that connect and disconnect randomly.
    Sometimes they talk to the cRIO-Server at the same time and the commands come from both clients.

    Thanks!

    Cheers
    Niels Göran

     
  • Niels Göran Blume

    Quick follow-up:
    Since the LVZMQ lib is compiled with the draft option, I would assume that the SERVER-CLIENT pattern works. Is that correct?
    I couldn't find any corresponding functions like "send" with "routing_id"..
    Is this hidden somewhere or not yet implemented?

    Thanks!

     
  • Martijn Jasperse

    Hi,
    For particular sockets like ROUTER/DEALER, the routing_id is the first part of a multipart message. This is how a ROUTER server can ensure that a response gets delivered to the correct client when it managing multiple connections simultaneously. This is called the message "envelope" and is described here: https://zguide.zeromq.org/docs/chapter3/#The-Simple-Reply-Envelope, in particular figures 28 and 29.

    The idea is that you receive a multipart message which starts with the identity blob, process the rest of the message, and then send the response using that identity blob at the start. I see there's no example VI yet that shows how this works, but I should be able to throw one together when I get some spare time.

    Cheers,
    Martijn

     
  • Niels Göran Blume

    Hello,

    thanks for your answer!
    I came across the message envelope, but also, since the lib is compiled (can be compiled) with DRAFT enabled, I found the SERVER / CLIENT pattern, which is considered an "non-blocing" version of the ROUTER / DEALER pattern.

    I changed the "zmq_labview.c" file as follows by adding a recv_server function:

    EXPORT int lvzmq_recv_server( sock_obj **pinstdata, sock_obj *sockobj, UHandle h, int *flags, unsigned int *msg_routing_id )
    {
        int ret = 0;
        zmq_msg_t msg;
        void *sock;
        DSSetHSzClr( h, 4 ); /* clear the output handle */
        CHECK_SOCK( sockobj );
        /* is this already blocking? */
        if ( sockobj->flags & FLAG_BLOCKING ) return -EINPROGRESS;
        sock = sockobj->sock;
        /* create a message object */
        zmq_msg_init( &msg );
        /* prepare for blocking call */
        if ( pinstdata ) *pinstdata = sockobj;
        sockobj->flags |= FLAG_BLOCKING;
        DEBUGMSG( "RECV on %p into %p", sockobj->sock, h );
        ret = zmq_msg_recv( &msg, sock, flags ? *flags : 0 );
        DEBUGMSG( "  RECV ret %d", ret );
        sockobj->flags &= ~FLAG_BLOCKING;
        /* was the call terminated? */
        if (( ret < 0 ) && ( zmq_errno() == ETERM )) {
            /* if it was an interrupt, we MUST close */
            DEBUGMSG( "TERM during RECV on %p", sockobj->sock );
            if ( sockobj->ctx )
                if ( sockobj->ctx->flags & FLAG_INTERRUPT )
                    lvzmq_close( sockobj,1 );
            if ( flags ) *flags = 0;    /* make sure flags=0 for zmq_recv_multi */
            zmq_msg_close( &msg );      /* prevent memory leak */
            return -ETERM;
        }
        if ( pinstdata ) *pinstdata = NULL;
        /* is there more to the message? */
        if ( flags ) *flags = ( zmq_msg_more( &msg ) > 0 );
        /* was it success? */
        if ( ret >= 0 ) {
            int l = ( int )zmq_msg_size( &msg );    
            DSSetHandleSize( h, l+4 );
            *( u32* )*h = l;
            memcpy( *h+4, zmq_msg_data( &msg ), l );
            // Obtain msg_routing_id from SERVER-CLIENT socket pattern
            *msg_routing_id = zmq_msg_routing_id ( &msg );
        }
        zmq_msg_close( &msg );
        return RET0( ret );
    }
    

    That seems to work, since I get a number != 0 for the msg_routing_id.

    But...
    I also added the following "send_server" function, which has the msg_routing_id as an input and sets this for the message prior to sending.

    EXPORT int lvzmq_send_server( sock_obj *sockobj, const UHandle h, int *flags, unsigned int *msg_routing_id )
    {
        int ret = 0;
        int ret_routing_id = 0;
        uint32_t l = 0;
        zmq_msg_t msg;
        CHECK_SOCK( sockobj );
        if ( h ) {
            l = *( u32* )*h;
            // use msg_init_size which maintains an efficient stack for small messages
            ret = zmq_msg_init_size( &msg, l );
            if ( ret < 0 ) return RET0( ret );
            memcpy( zmq_msg_data( &msg ), *h+4, l );
        } else {
            // empty message 
            zmq_msg_init( &msg );
        }
        // ====================================================
        // set msg_routing_id  prior to sending message
        // if ( msg_routing_id && *msg_routing_id ) { 
            ret_routing_id = zmq_msg_set_routing_id( &msg, msg_routing_id );
        // }
        // ====================================================
        ret = zmq_msg_send( &msg, sockobj->sock, flags ? *flags : 0 );
        if ( ret < 0 ) {
            DEBUGMSG( "SEND FAILED %p (%u), %d", sockobj->sock, l, ret );
        }
        if ( flags ) *flags = 0; // unused
        // make sure we always clean up the msg
        zmq_msg_close( &msg );
        return RET0( ret );
    }
    

    Unfortunately, this always return with "EHOSTUNREACH: No route to host"..
    I'm not sure, what's wrong here, but maybe you can give me some pointers...
    I also added the corresponding VIs, I created, maybe something is wrong there..
    (not much experience with calling C libs on Linux-RT 64-bit)

    Next step would be to try to get the "ret_routing_id" out and back to LabVIEW for some debugging...

    Thanks !

    Cheers
    Niels Göran

     

    Last edit: Niels Göran Blume 2022-03-30
  • Niels Göran Blume

    Hej hej,

    any comments / ideas regarding this ?
    Thanks !

    Cheers
    Niels Göran

     
  • Martijn Jasperse

    Hi,
    Sorry I didn't get around to looking at this more, I have been swamped with work the past few months. I haven't worked with any options since DRAFT was added, as there is additional complexity there. My understanding of ZMQ paradigms probably counts as "classic" now, since those are the network structures that led me to wrap the library in the first place. The paradigms can be a bit confusing, particularly as the example implementations tend to use blocking calls. Hope you were able to work something out.

    Cheers,
    Martijn

     
  • Niels Göran Blume

    Hello Martijn,

    unfortunately I haben't made any progress whatsoever in the last months due to work.
    I still need to perform that debugging mentioned.
    Nevertheless, I think, that I will for now adhere to Request-Reply and just handle connection after each other.

    Any other ideas are welcome ;-)

    Cheers
    Niels

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.