DMDX Help.

Send Keyword

<Send string>
<Send "<xmlstring value=''N''>">

    Switch that sends a string to a TCP/IP socket.  Item file must also have selected <InputDevice tcpip>.  The string will have a carriage return and line feed added to the end of it and then be sent over the network.  In order to send XML strings the string needs to be double quote delimited because XML contains angle brackets that are of course part of DMDX's syntax and then to add insult to injury XML often contains double quote delimited values as well making it quite tough to get XML strings through the DMDX parsing process so two single quotes next to each other will be collapsed to a single double quote (and if you use a font other than a mono-spaced one like Courier New that I use for DMDX syntax examples two quotes next to each other actually look like a double quote '' for example).  Similarly while not technically needed for XML I'll bet if I don't enable it people will also need to send a semi-colon that is similarly impossible to get past DMDX's parsing.  Here the sequence period comma will be collapsed into a semi-colon.  Sure hope those sequences don't come back to bite me later on but the need to send either '' or ., would appear to be quite low and if not I'll just come up with another escape sequence when it's needed...  For instance to have a Gazepoint eye tracker inject strings into it's saved data using their Open Gaze API the following send keyword can be used:
<send "<SET ID=''USER_DATA'' VALUE=''ITEM 987'' />">

    The following, while not XML and thus not needing double quote delimiters, will send "&amp;":
<send ''&amp.,''>

    The following item file opens a socket on the local machine using the default port 4242 that the Open Gaze API uses and enables a number of fields from the eye tracker to be returned and written to the output file:

<ep> <azk> <cr> <fd 12> <msd period 1200> <t 1100>
<vm desktop> <id Keyboard> <mpr +Left> <mnr +Right> <nfb> <id tcpip> </ep>
0 <send "<SET ID=''USER_DATA'' VALUE=''0'' />"> <fd 1> <! initially set user data to zero as it's value can run on from the previous run> /
    <send "<SET ID=''ENABLE_SEND_COUNTER'' STATE=''1'' />"> <fd 1> /
    <send "<SET ID=''ENABLE_SEND_TIME'' STATE=''1'' />"> <fd 1> /
    <send "<SET ID=''ENABLE_SEND_PUPILMM'' STATE=''1'' />"> <fd 1> /
    <send "<SET ID=''ENABLE_SEND_USER_DATA'' STATE=''1'' />"> <fd 1> /
    <send "<SET ID=''ENABLE_SEND_PUPIL_LEFT'' STATE=''1'' />"> <fd 1> /
    <send "<SET ID=''ENABLE_SEND_DATA'' STATE=''1'' />">
111 <send "<SET ID=''USER_DATA'' VALUE=''ITEM 111'' />"> <ms% 1000> "ITEM 111" /;
222 <send "<SET ID=''USER_DATA'' VALUE=''ITEM 222'' />"> <ms% 1000> "ITEM 222" /;
333 <send "<SET ID=''USER_DATA'' VALUE=''ITEM 333' />"> <ms% 1000> "ITEM 333" /;
0 "Done" <send "<SET ID=''ENABLE_SEND_DATA'' STATE=''0'' />"> ;

    One note about the timing of <send> frames and that is that as the code currently stands DMDX doesn't hang around and wait to make sure the network sent the last string so while you could conceivably stuff all those initialization commands into a series of frames with zero tic durations (for instance using the comma frame separator) if the thread that sends the data hasn't begun sending the previous string the next one will overwrite it and the previous one will be lost (you'd also get a memory leak but almost no machine these days is going to be negatively impacted by that).

    As far as testing <send>'s functionality is concerned you can use the Monitor application that DMDX uses to monitor a run from another machine, by default it uses port 27100 so you can either set it up on another port or specify the port number in your <id tcpip> keyword with <id tcpip> for instance.  While Monitor will not send any replies back (I used a special Debug build of it to do the testing needed to get the whole tcpip device / <send> functionality going) you can at least see what strings it is you'll be sending and hell, if people really want it I can expose a testing interface for wider use.

    If you want to parse replies from a machine in the .tcpipreply. macro I built an example parsing replies from that Debug version of Monitor.  When I send it a <requestack num=24 delay=1000 subsequentdelay=300> request it will respond with the requested number of replies of the form <ACK number="1"> and we parse out the numeric value between the quotes and display it using the newly added character constants in the expression evaluator to find the field we want (although here we could just look for double quotes as there are no other fields but the point of this is exposition and not brevity) and stop when we see the number 24.  There are few special gotchas here, first off we're setting the macro .tcpipreply. empty to begin with as we will probably be running before the first reply is received and would get a reference to an undefined macro unless we first set it up.  Second we operate on a copy of .tcpipreply. in T in case a new reply comes in as we're processing it.  Another thing is we can't put a double quote in a character constant, DMDX special characters will cause syntax errors as (at this stage at least) I'm not making the rest of DMDX's parsers ignore what's in single quotes as well as double quotes.  So instead we go and look up what the ASCII decimal code for a double quote is and see that it's 34 and just hard code it, hey at least I didn't have to look up the other letters in "number=".  Lastly we just run processing .tcpipreply. repeatedly not caring if we've already processed a given reply, if we wanted to block that item 2250 would set .tcpipreply. empty again with m.tcpipreply.++.  Of course if you were going to use this code you'd make item 2250 a <return> item and you'd call item 1111 each time you wanted to know what the tcpip host last sent...

<ep> <cr> <nfb> <safemode 1> <fd 10>
<id tcpip> <!branchdiagnostics>
0 "test tcpip reply parsing"
    m.tcpipreply.++ <set c1=0>;
1 <SEND "<requestack num=24 delay=1000 subsequentdelay=300>">
    "send <requestack num=24 delay=1000 subsequentdelay=300>";
~1111 mT+~.tcpipreply.+;
~2222 <macro pop T, c1> <bi 1111, c1 .eq. 0>;
~1 <bi 2222, c1 .ne. 'n'>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. 'u'>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. 'm'>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. 'b'>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. 'e'>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. 'r'>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. '='>;
~1 <macro pop T, c1> <bi 2222, c1 .ne. 34> <!double quote>;
~1 mN++ <! extract the field we want into N>;
~2230 <macro pop T, c1> <bi 1111, c1 .eq. 0>;
~1 <bi 2240, c1 .eq. 34> <!double quote>;
~1 <macro push N, c1> <bu 2230>;
~2240 mm++ <! reverse N before displaying it>;
~2245 <macro pop N, c1> <bi 2250, c1 .eq. 0>;
~1 <macro push M, c1> <bu 2245>;
2250 d2 "~M" <bi 1111, ~M .ne. 24>;
0 "Done";

    If you wanted to parse POG values and determine if the subject is looking at a particular display the <storecoords> keyword can be used.


DMDX Index.