#include <sock_func.h>
#include "add.h"
main () {
struct sockaddr_in name; //
Internet socket name (addr)
int sd = sock_init("localhost",50008,AF_INET,SOCK_STREAM,
(struct sockaddr *)&name, 0);
if (sd<0) printf("Couldn't open socket\n");
if (connect (sd,&name, sizeof(name)) <0)
{
perror("Error when connecting:");
return -1;
}
float pos[3],dir[3];
for (int i=0; i<3; pos[i] = dir[i] =0.0,
i++);
printf("written:%d\n",add_send(sd,"cube","world","shape:cube",pos,dir,1));
close(sd);
}
The message must be XDR-encoded. As long as the messages written to
the socket port follow this format (and are XDR encoded), then any client
that is capable of doing TCP-sockets and XDR can connect to Viz. For convenience,
we provide a C++ or Python API.
Client must use the outputControl message to subscribed/unsubscribe to messages. Queries are special messages that don't require the client to subscribe. These queries allow the client to query Viz and get an answer (output message) without having to subscribe before and unsubscribe after sending the query.
Output message format:
<bytes count>,<message name>,<message body>\n
<bytes count> is a 6-digits integer.
<message name> is a string containing
no ','.
<message body> is a string and depends
on the message.
Note that the byte count only includes "<message name>,<message body>" and doesn't include the 1st "," (between <bytes count>,<message name>).
The output message is NOT XDR-encoded.
Message name | Description | Message body (data) | Output when: |
polyline | Polyline points coordinates + normals. | <number of points>,<x1>,<y1>,<z1>, <nx1>,<ny1>,<x2>,<y2>,... | Polyline is closed or queryPolyline |
all | By subscribing to all, the client receives all possible messages. | <message name> and <message body> are sent unchanged. | When any message is output. |
object_added | New object | <object name>,<URL> (source of the object) | An object is added |
object_removed | Object removed | <object name> | An object is removed |
selection | Objects selected by user | <number of objects selected>, <object name>, <object name>, ... | An object is selected
Middle button pressed in selection mode. |
ray_pick | Ray picked performed | <name>, <x>,<y>,<z>,<nx>, <ny>, <nz>
Note: <name> is "None" if no intersection. |
Ray pick query |
scene_objects | Objects in the scene | <name>,<name>,... | queryScene is sent. |
viewer_param | Viewer parameters | <viewer number>,<param1>,<param2>,... | queryVParam |
state | Viz state | <viz mode>,<viewer mode number> | queryState |
pose | Object pose | <object name>,<x>,<y>,<z>, <thetaX>, <thetaY>, <thetaZ> | queryPose |
Interactive clients such as measurement tools are heavily based on two-way communication with Viz. For instance, they generally issue a setPolyline command to allow the user to draw a polyline in Viz. Then they wait for the polyline to be finished, and use the polyline information to compute the measurement value. Depending on how flexible and versatile the user intends the client to be, communication can be synchronous (using blocking read on socket ports), asynchronous (by polling ports) and one client can integrate multiple measurement capabilities.
Should then the client use one separate connection for each tool or
output message ? The general rule of thumb is:
Keep the number of socket connections low (ideally one) unless:
- you have concurrent operations using the same output message type
(overlapping in time and on the same socket).
- you intend to read blocking for output messages on some connections
and poll other connections.
- you need have messages executed in parallel by the Viz server.
In addition, the Python API provides extended comm capabilities:
- parsers: a set of parsers for different output messages will return
a dictionary of parsed information.
- the MessageHandler class for non-blocking
reads.
The MessageHandler class allows the client to attach/detach callbacks to specific output message types. Suppose you have a client that integrates two modules m1 and m2 which are interested in the same output message 'polyline'. You want m1, respectively m2, to react and call a callback f1, respectively f2, whenever a polyline message arrives from the server on the socket. If you're not careful, you will have m1 then m2 poll the socket. Whenever a message arrives when m1 is polling, m1 will read that message and call f1. However, when it's m2's turn to check that socket, no more message is there (since it's been "consumed" by m2) and f2 will not be called as it should be.
This situation can be avoided with minimal developing effort by using
the MessageHandler. The MessageHandler
should be instantiated once for each socket connection that needs to be
polled. The client can then attach all callbacks to specific message types
and detach them at any time. Whenever a message arrives on the socket,
the MessageHandler calls every callbacks
attached to the message type in the order they were attached. The MessageHandler
can also parse the message if needed. If no callback is attached, the MessageHandler
will not poll.