0
votes

for my Thesis I need to connect a Head-Tracking sensor to an exisiting car simulator software. The car simulator is able to receive TCL commands via DDE and so far I have successfuly established a communication channel. The idea is to use the data from the sensor to adjust the current view on the simulator window.

The sensor data is processed with the help of a c++ application that also acts as DDE client and sends the command string to the DDE server (the simulator). Using an example from Microsoft (see here: http://support.microsoft.com/kb/279721/en-us) I'm sending the command string with the following line:

DdeClientTransaction((LPBYTE)hData, 0xFFFFFFFF, hConv, 0L, 0, XTYP_EXECUTE,  
TIMEOUT_ASYNC, NULL);

The variable hData represent after some other function calls the string to transfer to the TCL interpreter on the simulator. The command string I'm sending recursively is exclusive to the simulator and looks as follows:

"Movie eval {dict set View(0:0) xrot %s; dict set View(0:0) yrot %s; dict set View(0:0) zrot %s;}"

In case you wonder %s are the placeholders for the yaw,pitch and roll of the headtracker. Everything is working correctly. However I got the feeling that the TCL interpeter of the simulator is sometimes to slow and ends up hanging the whole simulator.

On the DDE side, the DdeClientTransaction-function returns a value whether the transaccion was successful/busy and I'm using this to avoid spamming the DDE server (simulator), so I suspect the issue is the TCL interpreter being to slow.

My question is now: Is there a TCL command I can add into my command string or another way to have the interpreter process the next received command via DDE once it's done with the previous one, and incase there are more commands queued up, just process the newest one?

I've noted that sending update;:

"Movie eval {dict set View(0:0) xrot %s; dict set View(0:0) yrot %s; dict set View(0:0) zrot %s; update;}"

is more stable, but I don't quite understand what it does.

1

1 Answers

0
votes

What the update does is to process events immediately. It drains the event queue of everything that is currently there, keeping going until it would have to enter a wait state for the next event to come. (Internally, update is a thin wrapper round an API function called Tcl_DoOneEvent().)

It's usually not recommended. The issue is that if the event that caused the update to happen inside its processing script happens again, the update will fire again and you can easily end up with update inside update inside update inside … which will fill up the C-level stack fairly rapidly with all the calls to Tcl_DoOneEvent() which in turn — circuitously — calls back to Tcl_Eval()

If things are feeling sluggish, that's usually a sign that your event handlers are taking too long to process. The best thing would be if you could shorten the amount of time that any handler runs for, and possibly also reduce the rate at which you're pushing events into the system. This will keep to a minimum the amount of time between when your little script runs and when the redraw in response happens.

But that might be quite hard to do. (I don't know; it depends on what else is going on, etc.)

Redraws typically happen in idle events, which are a special event queue that is only drained when Tcl_DoOneEvent() has nothing else to be doing. You can try forcing things along a bit with less possibility of catastrophe by changing the update to update idletasks, which will just drain the idle event queue. (NB: you can add things to the idle queue using after idle; most of the time you don't need to do that.) It won't handle redraws driven by outside events (such as iconification or window resizing) but that's often less important to handle rapidly.

I suppose you could try playing around with after idle, but I suspect that that is likely to be unsatisfactory to you. I only know of a few places where it makes any real sort of sense to use (and they're when you're doing low-level messing around with a widget implementation).