FreeWRL EAI Interface Design and Function.

December 2, 2008

John Stewart, CRC Canada

With notes from Dave Joubert.

 

Definitions:

 

EAI = External Authoring Interface; to control the X3D Scene Graph from an external (Java) program. FreeWRL supports the EAI, with the exception of the two update methods. http://freewrl.sourceforge.net/EAI.html

 

SAI = Scene Authoring Interface; a set of Java methods to extend the functionality of SAI. FreeWRL supports external SAI via java programs. See Web3d.org website.

 

MIDI – A digital data bus used mainly for real-time rendering of music. See www.midi.org.

 

Introduction:

 

The EAI was originally a Java-based method of controlling a VRML Scene-graph. FreeWRL has had Java/EAI functionality for approximately one decade. The EAI code within FreeWRL has been expanded to handle external SAI commands.

 

However, the method of implementation lends itself to allowing control of the X3D/VRML (hereafter just referred as X3D) Scene-graph from other languages and applications (possibly from other nodes in a network) so it is not just Java based. For instance, the MIDI implementation from CRC uses this methodology.

 

This document is targeted at those who are interested in experimenting with control of X3D, or having X3D control external applications. Some esoteric commands do not yet have supporting documentation, but what is here should enable one to fully control an X3D Scene Graph.

 

 

 

Method of communication:

 

Client/server based.

 

Usual method:

Controlling command from program ˆ FreeWRL;

FreeWRL sends acknowledgment ˆ controlling program.

 

We will describe this command/ack protocol here.

 

 

When FreeWRL is started, eg from the following script:

 

freewrl root.wrl --eai &

java  EAIAddRemove

 

the following happens:

-       FreeWRL starts, reads its command-line parameters, and goes into ÒbackgroundÓ mode via the shell command Ò&Ó;

-       FreeWRL sees the Ò—eaiÓ parameter, opens a socket via the internal ÒconEAIorCLASSÓ function;

-       FreeWRL then listens to the socket for commands from an external application. The following is the actual code for this open:

 

servaddr.sin_family      = AF_INET;

            servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

            servaddr.sin_port        = htons(EAIport+socketincrement);

while (bind((*EAIsockfd), (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {

                        loopFlags &= ~NO_EAI_CLASS;

                        return FALSE;

                }

 

-       Where EAIport=9887, and socketincrement=0.

-       In the Java EAI implementation, the code to open the Java side (in FreeWRLEAI.java) is:

 

 

while (EAISocket == null) {

  try {

    sock = new Socket("localhost",9877);

  } catch (IOException e) {

É

 

try {

EAIin = new BufferedReader( new   InputStreamReader(sock.getInputStream()));

            } catch (IOException e) {

              System.out.print ("error reiniting data input stream");

            }

 

-       FreeWRL will continue to operate while socket communication is established.

-       Command/responses are outlined below.

 

 

Notes from Dave Joubert:

 

(Dave wrote a TCL interface to FreeWRL, using this EAI socket interface)

 

The EAI is inherently Sync and Async and this is critically important if you decide to write this as a single threaded model without Listeners.

Any incoming packets could be bigger than your socket buffer. You will have to accumulate your socket reads into a complete packet first before taking any action.

Do not give in to the temptation of writing it as an async model. You will soon hit the problem where the main code thread sets up an object, and then tries to use it. If you had not yet received the expected reply from the initial command, the second command will fail.

 

So, you need to block immediately after sending a packet and wait for a reply if the command requires it. Each command that is about to call the IO routine, needs to probably pass at least three pieces of information: what to send, whether a reply is expected, and what to do with the reply. (You could handle the reply in the caller, but because the IO routine will have to cope with events, and because a lot of the reply-handling code will be similar, it is best to do it in two stages)

 

The blocking has to be inside a loop, that can re-block if it needs to, because:

A) You might not yet have the whole reply

B) You may have got an event packet while waiting for the reply

 

(B) is very important if you are building a large interactive world. It costs a lot of time to build the whole thing in two separate loops, ie build all the objects with the required sensors and loop through again and make all the sensors active. Conversely, if you build it in one loop, your user is bound to wave his/her mouse around while it is building, thus triggering an event.

 

The best approach I found when you get an event packet while waiting for a reply packet, is to queue the event packets until all the replies have been received. This is critically important when you do something complicated in response to an event, like change the colour of an object, because then you will be waiting for a reply on that request as well as waiting for the first reply.

 

You also need to keep the event buffer safe, ie do not empty it out prior to blocking for a reply, and do not leave the main IO loop until the event buffer is empty.

 

 

 

 

Commands sent from the application to FreeWRL

 

Overview: (Specifics are found further down this document)

 

All commands/data are sent/received as ASCII data. While this may seem inefficient, it is not only easier to debug, it is easier to create applications in different operating systems. Besides, most commands are short, smaller than the minimum packet size, so network efficiency is not a reason for binary data.

 

Note however, that the FreeWRL side expects commands in a STRICT format, otherwise errors will occur. Make sure that the syntax of data is correct, because error recovery is sparse, at best.

 

FreeWRL will accept the following list of commands:

 

#define GETNODE         'A'

#define SENDCHILD       'C'

#define SENDEVENT       'D'

#define GETVALUE        'E'

#define GETFIELDTYPE    'F'

#define REGLISTENER     'G'

#define ADDROUTE        'H'

#define REREADWRL       'I'

#define DELETEROUTE     'J'

#define GETNAME         'K'

#define GETVERSION      'L'

#define GETCURSPEED     'M'

#define GETFRAMERATE    'N'

#define GETURL          'O'

#define REPLACEWORLD    'P'

#define LOADURL         'Q'

#define VIEWPOINT       'R'

#define CREATEVS        'S'

#define CREATEVU        'T'

#define STOPFREEWRL     'U'

#define UNREGLISTENER   'W'

#define GETRENDPROP     'X'

#define GETENCODING     'Y'

#define CREATENODE      'a'

#define CREATEPROTO     'b'

#define UPDNAMEDNODE    'c'

#define REMNAMEDNODE    'd'

#define GETPROTODECL    'e'

#define UPDPROTODECL    'f'

#define REMPROTODECL    'g'

#define GETFIELDDEFS    'h'

#define GETNODEDEFNAME  'i'

#define GETROUTES       'j'

#define GETNODETYPE     'k'

#define MIDIINFO        'l'

#define MIDICONTROL     'm'

 

and the following DATA types:

 

#define EAI_SFBool              'b'

#define EAI_SFColor             'c'

#define EAI_SFFloat             'd'

#define EAI_SFTime              'e'

#define EAI_SFInt32             'f'

#define EAI_SFString            'g'

#define EAI_SFNode              'h'

#define EAI_SFRotation          'i'

#define EAI_SFVec2f             'j'

#define EAI_SFImage             'k'

#define EAI_MFColor             'l'

#define EAI_MFFloat             'm'

#define EAI_MFTime              'n'

#define EAI_MFInt32             'o'

#define EAI_MFString            'p'

#define EAI_MFNode              'q'

#define EAI_MFRotation          'r'

#define EAI_MFVec2f             's'

#define EAI_MFVec3f             't'

#define EAI_SFVec3f             'u'

#define EAI_MFColorRGBA         'v'

#define EAI_SFColorRGBA         'w'

#define EAI_MFBool              'x'

#define EAI_FreeWRLPTR          'y'

#define EAI_MFVec3d             'A'

#define EAI_SFVec2d             'B'

#define EAI_SFVec3d             'C'

#define EAI_MFVec2d             'D'

#define EAI_SFVec4d             'E'

#define EAI_MFDouble            'F'

#define EAI_SFDouble            'G'

#define EAI_SFMatrix3f          'H'

#define EAI_MFMatrix3f          'I'

#define EAI_SFMatrix3d          'J'

#define EAI_MFMatrix3d          'K'

#define EAI_SFMatrix4f          'L'

#define EAI_MFMatrix4f          'M'

#define EAI_SFMatrix4d          'N'

#define EAI_MFMatrix4d          'O'

#define EAI_SFVec4f             'P'

#define EAI_MFVec4f             'Q'

#define EAI_MFVec4d             'R'

 

 

An example of the data flow using the FreeWRL test EAIAddRemove.java:

 

// open communication

Browser browser = Browser.getBrowser(this);

if ((browser) == null) {

System.out.println("FATAL ERROR! no browser");

}

 

// get a node from the FreeWRL X3D scene graph

Node root = browser.getNode("ROOTNODE");

 

// Instantiate (get handle to) the EventIn objects

addChildren = (EventInMFNode) root.getEventIn("addChildren");

removeChildren = (EventInMFNode) root.getEventIn("removeChildren");

 

// Instantiate a lovely blue ball

shape1 = browser.createVrmlFromString(

         "Transform{translation -2.3 2.1 0 children Shape{\n"+

         "  appearance Appearance {\n" +

         "    material Material {\n" +

         "      diffuseColor 0.2 0.2 0.8\n" +

         "    }\n" +

         "  }\n" +

         "  geometry Sphere {}\n" +

         "}}\n");

 

 

Getting FreeWRL X3D node ÒROOTNODEÓ

 

Application sends:

1A ROOTNODE

 

Which is:

-       command sequence number 1;

-       command ÒAÓ – GETNODE; (see table above)

-       node name in the X3D Scene Graph: ÒROOTNODEÓ.

 

FreeWRL replies with:

RE

1227295777.240668

1

0 8724736

RE_EOT

 

Which is:

-       a response (ÒREÓ), as opposed to an asynchronous event (ÒEVÓ);

-       system time;

-       Ò1Ó = sequence number; should match the command sequence number;

-       2 node ÒhandlesÓ, currently a pair of (0, memoryPtr), but the actual meaning might change – just treat these as constants.

-       ÒRE_EOTÓ – response END OF TEXT.

 

 

Getting FreeWRL X3D node EVENTIN

 

2F 0 8724736 addChildren eventIn

 

Which is:

-       command sequence number 2;

-       command ÒFÓ – GETFIELDTYPE;

-       node handle pair;

-       field of node;

-       eventType.

 

FreeWRL returns:

 

RE

1227295777.255864

2

8724736 120 -10 q 0 eventIn

RE_EOT

 

Which is:

-       a response (ÒREÓ), as opposed to an asynchronous event (ÒEVÓ);

-       system time;

-       Ò2Ó = sequence number; should match command sequence number;

-       a line containing:

o      Ò872..Ó part of the node handle,

o      Ò120Ó offset of data in the node (treat as constant)

o      Ò-10Ó data length for routing; negative numbers mean that they are varying size, as opposed to a specific number of bytes;

o      ÒqÓ = EAI_MFNode – the type of the addChildren field;

o      Ò0Ó FreeWRLs internal type;

o      ÒeventInÓ – telling the application what type FreeWRL thinks this is.

-       and, the ÒEOTÓ marker.

 

FreeWRL sending in x3dv Code:

 

4S Transform{translation -2.3 2.1 0 children Shape{

  appearance Appearance {

    material Material {

      diffuseColor 0.2 0.2 0.8

    }

  }

  geometry Sphere {}

}}

 

EOT

 

Which is:

-       command sequence number 4;

-       command ÒSÓ – CREATEVS (create VRML from string)

-       input text

-       the string ÒEOTÓ at the START of the line.

 

Response:

 

RE

1227295777.271929

4

0 9152048

RE_EOT

 

Which can be decoded EXACTLY as for the ÒROOTNODEÓ command, above.

 

Adding, removing this child:

 

É

addChildren.setValue(shape1);

É

removeChildren.setValue(shape1);

 

Sent:

10C 8724736 120 addChildren 9152048

and

16C 8724736 120 removeChildren 9152048

 

both return responses, eg:

 

RE

1227295777.326321

10

0

RE_EOT

 

 

Command Specifics.

 

In this section, we will discuss individual commands and responses.

 

Please Note:

-       As FreeWRL was initially Perl based, then C based, sometimes extra data is provided in commands/responses. It is easier to leave obsolete data in at times than to remove it, risking version problems. You will see this in the node handle pairs; certain times you will have a response pair (eg, Ò0 44535Ó) other times you will just see the second part of this pair – (Ò44535Ó) this is a relic of the Perl code.

 

-       If the source code and this document do not agree, then the source code is correct, and you have been granted the right to complain and/or fix the documentation! Please!

 

GETNODE         'A'

 

Description:   Goes through the DEF nodes, and finds one matching the string.

 

Expects:         A valid DEF name for the current SceneGraph. eg:

 

                                    1A ROOTNODE

 

Returns:         A Ònode pairÓ in a standard response format – eg:

 

 0 8724736

                       

The first number is obsolete and can be ignored; the second                           number is the actual node ÒhandleÓ.

 

 

 

SENDCHILD       'C'

 

Description:   send a node handle to the node/offset of a parent.

 

Expects:

10C 8724736 120 addChildren 9152048

 

which is:

-                sequence number + command;

-                node handle;

-                offset of field within node (see GETFIELDTYPE)

-                string ÒaddChildrenÓ or ÒremoveChildrenÓ

-                node to add/remove

 

 

Returns:         standard return; eg:

 

RE

1227295777.326321

10

0

RE_EOT

 

 

SENDEVENT       'D'

 

Description:   Sends an event to a field of a node.

 

Expects:         eg, for a SFVec3f:

                        12Du 9309664 212 0 1.0 2.0 3.0

 

Returns:         THERE IS NO RETURN VALUE – this lets many events

get sent quickly.

 

GETVALUE        'E'

 

Description:   Get the value of a field within a node.

 

Expects:         Node handle, field offset, type, and data size. Eg:

 

                        15E 9199216 168 i 16

                       

                        Where the ÒIÓ – EAI_SFRotation, and 16 = data size in bytes.

 

Returns:         field value.

 

                        RE

1227714490.500232

15

0.000000 0.000000 1.000000 9.531744

RE_EOT

 

Different field types have different return values.

 

SFInt32, SFVec*, SFColor*, SFFloat, SFTime int/float/double values follow the above structure.

 

MFInt32, MFVec*, MFColor*, MFFloat, MFTime int/float/double values follow the following structure: (example is a MFVec3f, 3 values)

 

RE

1227730825.741650

12

3

0.000000 0.000000 0.000000

1.000000 1.000000 1.000000

2.000000 2.000000 2.000000

 

RE_EOT

 

Other types are shown here:

 

SFBool:           either TRUE or FALSE

SFString:         eg: ÒStarting PositionÓ

MFString:       eg: Òstring 1Ó Òstring 2Ó

 

 

GETFIELDTYPE    'F'

 

Description:   Get the field type of the field of a node.

 

Expects:

2F 0 8724736 addChildren eventIn

 

Which is:

-       command sequence number 2;

-       command ÒFÓ – GETFIELDTYPE;

-       node handle pair;

-       field of node;

-       eventType.

 

Returns:

 

RE

1227295777.255864

2

8724736 120 -10 q 0 eventIn

RE_EOT

 

Which is:

-       a response designator (ÒREÓ);

-       system time;

-       Ò2Ó = sequence number; should match command sequence number;

-       a line containing:

o      Ò872..Ó part of the node handle,

o      Ò120Ó offset of data in the node (treat as constant)

o      Ò-10Ó data length for routing; negative numbers mean that they are varying size, as opposed to a specific number of bytes;

o      ÒqÓ = EAI_MFNode – the type of the addChildren field;

o      Ò0Ó FreeWRLs internal type;

o      ÒeventInÓ – telling the application what type FreeWRL thinks this is.

-       and, the ÒEOTÓ marker.

 

 

 

REGLISTENER     'G'

 

Description:   Places a route in the routing table so that changes get sent.

 

Expects:         eg: for a ProximitySensor position_changed event:

15G 9148224 180 u 12

 

ASYNC Callback Event:         Every time this route runs, the following will be sent:

 

EV

1227733533.828239

15

0.000000 0.000000 6.900000

EV_EOT

 

Note that this is an ÒEVÓ not a ÒREÓ and that the data can come any time between REs.

Note also that the EV returns the ORIGINAL sequence number of the REGLISTENER command; this sequence number is your handle to help you decode the meaning of each and every ASYNC Callback. (the handle number is "15", in the above example)

 

 

ADDROUTE        'H'

 

Description:   Adds a route to the routing table

 

Expects:         4 values, from node handle/field, to node handle/field. Eg:

 

10H 988342 fraction_changed 9824211 set_fraction

 

Returns:         In case of error, an error message will be printed on the console,

                        and a return value of Ò1Ó will be sent. (Ò0Ó = ok)

 

RE

1227633671.152842

10

0

RE_EOT

 

 

 

REREADWRL       'I'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

DELETEROUTE     'J'

 

Description:   Deletes a route to the routing table

 

Expects:         4 values, from node handle/field, to node handle/field. Eg:

 

10J 988342 fraction_changed 9824211 set_fraction

 

Returns:         In case of error, an error message will be printed on the console,

                        and a return value of Ò1Ó will be sent. (Ò0Ó = ok)

 

RE

1227633671.152842

10

0

RE_EOT

 

 

GETNAME         'K'

 

Description:   Get the name of the Browser (in this case, FreeWRL)

 

Expects:

                        7K

 

Returns:         one line containing the name of the browser. Eg:

 

 

RE

1227563622.598430

7

FreeWRL VRML/X3D Browser

RE_EOT

 

 

GETVERSION      'L'

 

Description:   Get the current (installed) version of FreeWRL

 

Expects:

                        6L

 

Returns:         one line containing the installed version of FreeWRL. Eg:

 

RE

1227563622.598430

6

V1.20.4

RE_EOT

 

 

 

GETCURSPEED     'M'

 

Description:   Get the current navigation speed.

 

Expects:

                        7M

 

Returns:         one line containing the current Nav speed. (have to fill in max

                        Value) minimum value is 0.0 – user is stationary. Eg:

 

 

RE

1227563622.598430

7

0.0

RE_EOT

 

 

 

Returns:

 

 

GETFRAMERATE    'N'

 

Description:   Gets the current frame rate in frames per second.

 

Expects:

                        8K

 

Returns:

RE

1227563622.598430

8

106.014858

RE_EOT

 

 

 

 

GETURL          'O'

 

Description:   Gets the url to the currently loaded world.

 

Expects:

                        6O

 

 

Returns:

RE

1227628201.224926

6

/Users/john/Desktop/freewrl/tests/AddRemove/root.wrl

RE_EOT

 

 

 

REPLACEWORLD    'P'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

LOADURL         'Q'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

VIEWPOINT       'R'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

CREATEVS        'S'

 

Description:   Parse (but do not add to scene graph) code.

 

Expects:         Valid x3dv classic code, followed by the string ÒEOTÓ on its own

line. Eg:

 

4S Transform{translation -2.3 2.1 0 children Shape{

  appearance Appearance {

    material Material {

      diffuseColor 0.2 0.2 0.8

    }

  }

  geometry Sphere {}

}}

 

EOT

 

Returns:         node handle pairs; first number of pair is ignored, but should

be zero. Eg:

 

RE

1227295777.271929

4

0 9152048

RE_EOT

 

 

CREATEVU        ÔTÕ

 

Description:   Parse a URL,  and return a list of node handle pairs.

 

Expects:         16T /FreeWRL/freewrl/freewrl/tests/1.wrl

 

Returns:         node handle pairs; first number of pair is ignored, but should

be zero. Eg:

 

RE

1227295777.271929

4

0 9152048

RE_EOT

 

 

STOPFREEWRL     'U'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

UNREGLISTENER   'W'

 

Description:   removes a registered listener.

 

Expects:         16W 9208928 180 u 12

 

Returns:

 

RE

1227733885.542056

16

0

RE_EOT

 

 

 

GETRENDPROP     'X'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

GETENCODING     'Y'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

CREATENODE      'a'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

CREATEPROTO     'b'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

UPDNAMEDNODE    'c'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

REMNAMEDNODE    'd'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

GETPROTODECL    'e'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

UPDPROTODECL    'f'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

REMPROTODECL    'g'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

GETFIELDDEFS    'h'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

 

GETNODEDEFNAME  'i'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

GETROUTES       'j'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

GETNODETYPE     'k'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

MIDIINFO        'l'

 

Description:   not documented yet

 

Expects:

 

Returns:

 

MIDICONTROL     'm'

 

Description:   not documented yet

 

Expects:

 

Returns: