Skip to main content

mathlink or wstp - Asynchronous Programming


Consider that you would like to listen for Raw WiFi Packets coming into your NIC. Since Mathematica doesn't have built-in functions for such low level programming (as far as I know), one alternative would be following the steps:



  1. launch an executable file through Mathematica: Run["ReceivePacket.exe > packet.tmp"]

  2. read output file "packet.tmp"


  3. do simple data analysis

  4. iterate loop (go back to step 1)


The principle of the executable file "ReceivePacket.exe" is simple. It waits for a single Raw WiFi Packet to arrive. When the packet arrives, the data is written to a dump file and the executable ends. Fortunately, the hard work has already been done by WinPcap, so all that is necessary is to compile the tutorial saving packets to a dump file (with minor modifications).


However, there is a significant drawback in this process. By the time Mathematica read the output file "packet.tmp" and re-launch "ReceivePacket.exe", several packets would be 'lost'. Anticipating possible comments: This happens not because the data analysis is time consuming (on the contrary, the data analysis is very simple) neither because window pop-up is slow (the process is in batch mode with no print to screen).


1st Issue
Is it possible to improve this process in order to decrease the amount of packet loss?


2nd Issue
Is it possible to use MathLink to improve this process?



Answer




Let me give you a very basic example, how you can employ an asynchronously running LibraryLink function for this specific task. I will not do any real packet listen, but only explain the general setup.


Usually, a LibraryFunction in Mathematica is written in C and wrapped with a very small boiler plate code that is needed to attach the function directly to the Mathematica Kernel. Once this library is compiled, it can be loaded by LibraryFunctionLoad which gives you a Mathematica function that can be called like a normal Mathematica function. Exactly like other functions, the library function blocks the Kernel until it is finished.


This would be a problem, because what you want is a packet listener that runs in the background, catching all network packets while you can analyse them one after another. Therefore, your library packet listener must not block your Kernel. For this purpose, Mathematica provides you with a heavily undocumented API for asynchronous tasks. What I tell you here can only be found out by trying and reading example code.


You need to study the C file very carefully


FileNames["async-tasks-repeating.c", {$InstallationDirectory}, Infinity]

and I will use the function IntBackgroundTask as an example here. That means, that our network packets are simple integers. Let me stop here and just present you a running example. After this, I will try to answer questions you might ask yourself. A very uncommon form of an answer, but lets give it a try:


libFile = First@FileNames["async-tasks-repeating.c", {$InstallationDirectory},
Infinity];
libMain = FileNameJoin[{DirectoryName[libFile], "async-examples-libmain.c"}];


<< CCompilerDriver`
lib = CreateLibrary[{libFile, libMain}, "asyncLib",
"IncludeDirectories" -> {DirectoryName[libMain]}];
packetListener = LibraryFunctionLoad[lib,
"start_int_repeating_background_task", {Integer, Integer}, Integer];

pauseInMilliseconds = 500;
packetCounterInit = 0;


taskID = Internal`CreateAsynchronousTask[packetListener,
{pauseInMilliseconds, packetCounterInit}, (packet = {##}) &]

Dynamic[packet]

Let's explain what we did:


What is this libMain file? It is common code that is used in many examples. Therefore, they put it into a separate file once and you have to compile it as well. Btw, giving "IncludeDirectories" in the CreateLibrary call is important so that "async-examples.h" can be found.



  • Attention: In version 9.0 the file "async-examples.h" seems to be missing completely. Therefore, please create a file "async-examples.h" in the same directory where libMain is located and copy the following content into it.



How do I know the argument types inside LibraryFunctionLoad? (1) Look into the C code of "start_int_repeating_background_task". You see that Args[0] and Args[1] (line 43-44) are accessed and both with getInteger. In the end, you see that the result is set as Integer as well (line 46). (2) You study carefully the Examples.m file in the following directory


FileNames["LibraryLink", {$InstallationDirectory}, Infinity]

This will help to clear a lot other questions too!


How does this CreateAsynchronousTask work? The basic approach is the following: Your library function sets up the background task by initializing the arguments for it. Then you need to call createAsynchronousTaskWithThread (line 45) which returns an unique taskID that you return as result. You don't just call this library function! You need to register it by calling Internal`CreateAsynchronousTask which will directly start the background task for you. The last argument there is an event function that is called, every time your C loop calls raiseAsyncEvent (line 29).


But I have network packets and not only integers. What should I do? If you look in the C code, you see that a DataStore is used (line 21,27,28) to store the result in each iteration of the loop. Please look into the file


FileNames["WolframLibrary.h", {$InstallationDirectory}, Infinity]

starting in line 133 you find all available functions for asynchronous tasks and DataStore. Instead of adding only an integer to your data store, you can add all basic types and even give them names for easier access. Therefore, let's say you do something like this with your data store


ioLibrary->DataStore_addNamedInteger(ds, "Data", eventdata);

ioLibrary->DataStore_addNamedString(ds, "Name", "Packets");

then the data you receive in Mathematica will look like this


{"Data" -> 8, "Name" -> "Packets"}

This is a very convenient way to send important information of your network packet back.


The last argument of Internal`CreateAsynchronousTask is confusing. Where do I put my packet analyzing code? The last argument of CreateAsynchronousTask is the call-back function that is called when a new result is pushed by raiseAsyncEvent in your asynch. task. This callback function gets 3 arguments and the correspond directly to the 3 arguments you have given raiseAsyncEvent. Let us look at its declaration in WolframLibrary.h


void raiseAsyncEvent(mint asyncTaskID, char* eventType, DataStore data)

Therefore, your call-back function should get 3 arguments:




  • First, the AsynchronousTaskObject itself that can be used to stop the running background task (or start it again).

  • The second argument is a string that you could use to e.g. indicate whether a bad packet arrived. The eventType can therefore be used to handle different situations in your call-back function.

  • The third argument is always a DataStore that contains data that can be used in Mathematica.


Therefore, instead of using (packet = {##}) & like I did which simply sets the variable packet for dynamic displaying, you could define a real function:


    analyzePacket[task_, "Offline", _] := StopAsynchronousTask[task];
analyzePacket[_, "Packet", data_] := DoSomethingWithData[data];
analyzePacket[_, "BadPacket", _] := DoNothing[];


In your C code packet sniffing loop, you make a discrimination when e.g. your WIFI is offline. Then you send something like raiseAsyncEvent(asyncObjID, "Offline, NULL), or when a normal packet arrived, you use raiseAsyncEvent(asyncObjID, "Packet", packetData), etc.


What else can I do with the AsynchronousTaskObject that you saved in the variable taskID? Well, we have 4 highlevel Mathematica function that you need to really control starting and stopping of asynchronous tasks:



Additionally, you should look at


Options[Internal`CreateAsynchronousTask]
(* {"PostRemoveAsynchronousTask" -> None,
"PostStartAsynchronousTask" -> None,
"PostStopAsynchronousTask" -> None,
"PreRemoveAsynchronousTask" -> None,
"PreStartAsynchronousTask" -> None,

"PreStopAsynchronousTask" -> None, "Repeating" -> False,
"TaskDetail" -> "", "UserData" -> None, "Visible" -> True} *)

Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....