en fr

Sysquake Pro – Table of Contents

Sysquake – Table of Contents

Sysquake for LaTeX – Table of Contents

Remote Procedure Calls

Sysquake's ability to solve problems can be extended with the help of external applications. This requires the capability of exchanging data and triggering actions across applications. There are basically two possible situations: Sysquake initiates the communication, or it receives requests from the other application. In the first case, LME functions executed by the different handlers defined in SQ files are usually sufficient. In case the communication mechanism is not supported directly by Sysquake, it can be implemented by libraries which add communication protocol layers on top of those existing in LME (for instance an SMTP layer on top of TCP/IP for sending email), or by extensions. This chapter describes means for Sysquake to act as the target of remote requests.

The generic name for sending a command which lets another process, possibly running on another computer, execute code, is Remote Procedure Call. It typically involves the identification of the remote application, sending a procedure (or function) identifier and arguments, and receiving results.

Sysquake uses two different mechanisms: OLE Automation on Windows, and XML-RPC on Mac OS X and Linux. The servers are disabled by default; an option to enable them is located in the Preference panel "Extensions". For security reasons, the XML-RPC server accepts local connections only.

User applications can use directly these low-level mechanisms. In addition, SysquakeLink, a Java package, gives a higher-level interface, common to all platforms. It is obviously especially suited to Java applications, but can also be used from other applications using Java Native Access (JNI); please see the Sun documentation for more informations.

General Description

The main goal of RPC is to let a separate application (the client) interact with an SQ file loaded in Sysquake by reading and changing variable. Multiple variable values can be read or changed in a single command. After each change, Sysquake redraws all figures so that they show the new state.

Other RPC commands let the client application load an SQ or SQD file, execute a command as it it were typed in the command window, get the list of variables of an SQ file or defined in the context of the command window, and ge the value of variables defined in that context.

When an SQ or SQD file is loaded in Sysquake, a new SQ file instance is created, associated with a unique instance ID represented by a positive integer number. The instance ID is returned to the caller when the file is loaded; it is used to identify the instance in later calls.

All versions of Sysquake support at least the following types: real double (with support for inf and nan), int32 and logical, scalar or 2-d arrays; and character strings.

Variable change notification

Remote Procedure Calls are always initiated by the client. An additional mechanism is provided so that Sysquake can notify the client about user actions in Sysquake. The client can provide some code which is executed whenever a variable is changed in a specified SQ file instance, i.e. when the user manipulates a figure or selects a menu entry. This code can contact back the client so that it request the new values of the variables it is interested in.

In the Java package SysquakeLink, notifications are implemented with local UDP connections. The following code is executed by Sysquake; port number port is chosen by the operating system to avoid conflicts with other services.

_fd_varChangedSQLinkNot = ...
    socketnew('localhost',port,socketset('Proto','udp'));
_fd_varChangedSQLinkNot,'var changed');
fclose(_fd_varChangedSQLinkNot);

OLE Automation on Windows

The list below enumerates all OLE Automation functions implemented by Sysquake. Their IDs are not specified, because they are subject to change in future versions.

Version(
 [out,retval]BSTR *v);
Get Sysquake version as a string.
Execute(
 [in]BSTR src);
Execute code in the context of the Command window.
GetVariableNames(
 [in]LONG instanceId,
 [out, retval]VARIANT *names);
Get the list of variables declared in the SQ file of an instance.
GetVariableValueByName(
 [in]LONG instanceId,
 [in]BSTR name,
 [out, retval]VARIANT *value);
Get the current value of the variable of an instance, specified by its name.
GetVariableValueByIndex(
 [in]LONG instanceId,
 [in]LONG index,
 [out, retval]VARIANT *value);
Get the current value of the variable of an instance, specified by its index. The variable index must correspond to the variable order defined by GetVariableNames; first variable has index 0.
GetVariableValues(
 [in]LONG instanceId,
 [in]VARIANT const *variableList,
 [out, retval]VARIANT *values);
Get the value of multiple variables of an instance, specified by their indices or names. Variable indices must correspond to the variable order defined by GetVariableNames; first variable has index 0.
Open(
 [in]BSTR filename,
 [out, retval]LONG *instanceId);
Open an SQ file and return its instance ID.
MaximizeMainWindow(void);
Make Sysquake main window full screen.
MinimizeMainWindow(void);
Minimize (hide) the Sysquake main window.
Quit(void);
Quit Sysquake.
Reload(
 [in]LONG instanceId);
Reload an instance (same as menu File>Reload).
RestoreMainWindow(void);
Restore the size of Sysquake main window after MaximizeMainWindow or MinimizeMainWindow.
SetVariableValueByName(
 [in]LONG instanceId,
 [in]BSTR name,
 [in]VARIANT const *value);
Change the value of a variable specified by name and let Sysquake update the figures.
SetVariableValueByIndex(
 [in]LONG instanceId,
 [in]LONG index,
 [in]VARIANT const *value);
Change the value of a variable specified by index and let Sysquake update the figures.
SetVariableValues(
 [in]LONG instanceId,
 [in]VARIANT const *variableList,
 [in]VARIANT const *values);
Change the value of multiple variables specified by indices or names and let Sysquake update the figures. Arrays variableList and values must have the same size.
Show(void);
Show Sysquake.
SetVarChangeNotification(
 [in]LONG instanceId,
 [in]BSTR src);
Install code which is called everytime the user changes the contents of variables by dragging an element in a figure or selects a menu.
GetLMEVariableNames(
 [out, retval]VARIANT *names);
Get the list of variables in the context of the Command window.
GetLMEVariableValueByName(
 [in]BSTR name,
 [out, retval]VARIANT *value);
Get the value of a variable specified by name in the context of the Command window.
GetLMEVariableValueByIndex(
 [in]LONG index,
 [out, retval]VARIANT *value);
Get the value of a variable specified by index in the context of the Command window.
GetLMEVariableValues(
 [in]VARIANT const *variableList,
 [out, retval]VARIANT *values);
Get the value of multiple variables specified by indices or names in the context of the Command window.

Note: to change a variable in the context of the Command window, Execute should be called.

XML-RPC on Unix

XML-RPC is a simple Remote Procedure Call protocol based on XML, a markup language similar to HTML but more flexible and with a more regular structure, and HTTP, the most common communication protocol used on the Web. XML-RPC implementations are widely available for different platforms and programming languages, including Sysquake itself. Sysquake's implementation adds support for the floating-point numbers inf and nan, represented as <double>inf</double> and <double>nan</double>, respectively.

This section describes the implementation used for controlling Sysquake from other clients, and especially to interact with SQ instances. This does not prevent other XML-RPC servers from running in the same instance of Sysquake, provided that different TCP/IP ports are selected. If Sysquake runs multiple times simultaneously on the same computer with the XML-RPC server, they must use different TCP/IP ports. A typical choice is 8900. Only priviledged users (typically user "root") can launch a server with a port below 1024.

The URL sent to the XML-RPC server (the part after the hostname and the port number) is ignored.

The following methods are implemented:

Method name: close
Arguments: instance ID (integer)
Result: true if successful, false if failed

Method name: execute
Arguments: command to execute (string)
Result: true

Method name: getLMEVariableNames
Arguments: none
Result: list of variables of the context of the command window (array of strings)

Method name: getLMEVariableValue
Arguments: variable name (string) or index (integer) or array
Result: value of the variable in the context of the command window, or array of values

Method name: getVariableNames
Arguments: instance ID (integer)
Result: list of variables of the specified instance (array of strings)

Method name: getVariableValue
Arguments: instance ID (integer), variable name (string) or index (integer) or array
Result: value of the variable, or array of values

Method name: load
Arguments: path of SQ file or SQD file (string)
Result: ID of the new instance (integer)

Method name: reload
Arguments: instance ID (integer)
Result: new instance ID

Method name: setVarChangeNotification
Arguments: instance ID (integer), code executed every time a variable of the specified instance if changed (string)
Result: true

Method name: setVariableValue
Arguments: instance ID (integer), variable name (string) or index (integer) or array, new value(s)
Result: true

Method name: version
Arguments: none
Result: Sysquake version (string)

AppleScript

macOS can send XML-RPC calls directly from AppleScript. For instance, running the script below in the Script Editor requests Sysquake version and displays it in a dialog box.

tell application "http://localhost:8888/"
  set v to call xmlrpc {method name:"version"}
end tell
display dialog v

Parameters are provided in a parameters property, which is itself an ordered list. The script below executes LME code.

tell application "http://localhost:8888/"
  set code to "plot(rand(10), rand(10), 'rgby');"
  call xmlrpc {method name:"execute",parameters:{code}}
end tell

Please see Apple documentation on AppleScript for more informations.

SysquakeLink Java Package

SysquakeLink is a cross-platform Java package which can be used to communicate easily with Sysquake. It supports native Java types and classes, such as double and String. To exchange informations with Sysquake, it relies on native functions (JNI) which implement OLE Automation on Windows or XML-RPC on other platforms.

SQLinkTool

SQLinkTool is a simple Java command-line utility for testing all the features of SysquakeLink. It is provided as a compiled class and as source code, so that it serves as an example of how SysquakeLink can be used in a real application.

To run SQLinkTool, open a shell window. With XML-RPC, SysquakeLink sets the TCP/IP port number of the value stored in the environment variable SQLINKPORT; depending on your shell, type export SQLINKPORT=8888 with sh or bash, or setenv SQLINKPORT 8888 with csh or tcsh (change the port number so that it matches the value set in Sysquake). Then type the following command:

java -classpath path com.calerga.sysquake.SQLinkTool args

where path is the path of SysquakeLink classes and args is one or more arguments from the following list:

-c var
Display a variable value everytime any variable changes in the SQ instance specified with a preceding argument -i.
-d duration
Duration for -c in milliseconds (default is 5000).
-e command
Execute a command in the context of the command window.
-g var
Display the value of variable var, given by index or by name. The variable context must be specified with argument -i placed before -g; otherwise, the command window is assumed.
-G var-list
Display the value of variables given by arguments var list, given by index or by name (not both). The list variables ends with the last argument or with a hyphen -. The variable context must be specified with argument -i placed before -g; otherwise, the command window is assumed.
-h
Display all possible arguments.
-i context
Specify the context of variables for -g, -G, -s and -S. The context is either the SQ instance ID or the word lme for the command window.
-l path
Load a new SQ file and display its instance ID. The path must be absolute or relative to the current working directory of Sysquake.
-n
Display the list of variables. The variable context must be specified with argument -i placed before -g; otherwise, the command window is assumed.
-r id
Reload an SQ file instance specified by its ID.
-s var value
Set variable var, given by index or by name, to value. The variable context must be specified with argument -i placed before -g; otherwise, the command window is assumed.
-S var/value-list
Set the value of multiple variables. The following arguments are pairs of variable names or index (not both) and values, ending with the last argument or with a hyphen -. The variable context must be specified with argument -i placed before -g; otherwise, the command window is assumed.
-v
Display Sysquake version.

Values can be double or boolean, scalar or 2-d arrays; or strings. Numeric and boolean values have the same syntax as in LME, with brackets, semicolons and commas for arrays; any value whose syntax is not recognized is considered to be a string.

Examples

In the following examples, the shell prompt is displayed as %. The path which follows -classpath should be replaced with the actual class pass, i.e. the directory which contains com. Here, for the sake of simplicity, we assume the most common case where Sysquake is installed in the default application directory for the platform and we change directory so that the class path is the current directory, denoted by a single dot. On non-Windows computers, we should also set the environment variable SQLINKPORT so that it matches the value specified for Sysquake (see above).

On Windows:

% cd "C:\Program Files\Sysquake\Java\JavaClasses"

On macOS (the actual class path is displayed in Sysquake's Preferences dialog, in tab Extensions>File paths):

% cd "/Applications/Sysquake/Sysquake.app/Contents/Resources/Java"
% export SQLINKPORT=8889

On Linux (type "export SQLINKPORT=8889" instead of "setenv SQLINKPORT 8889" if your shell is csh or tcsh):

% cd /usr/local/sysquake/Java/JavaClasses
% export SQLINKPORT=8889

Once this is done, commands are the same on all platforms. Sysquake should be launched. Then to check that everything is set up correctly, display the version of Sysquake:

% java -classpath . com.calerga.sysquake.SQLinkTool -v
Sysquake 3.5

In Sysquake, define two variables in the Command window:

M = magic(3);
b = true;

In the terminal window, display the list of variables defined in the context of Sysquake command window:

% java -classpath . com.calerga.sysquake.SQLinkTool -n
ans
M
b

Display the value of variable M:

% java -classpath . com.calerga.sysquake.SQLinkTool -g M
[8.0,1.0,6.0;3.0,5.0,7.0;4.0,9.0,2.0]

Display the value of variables M and b (the end marker - is superfluous here, because there are no additional arguments):

% java -classpath . com.calerga.sysquake.SQLinkTool -G M b
[8.0,1.0,6.0;3.0,5.0,7.0;4.0,9.0,2.0]
true

Load SQ file approx.sq (you should specify its absolute path or a relative path with respect to the current working directory of Sysquake). The result is the ID of the new instance.

% java -classpath . com.calerga.sysquake.SQLinkTool -l approx.sq
1

Display the list of variables of SQ file instance 1:

% java -classpath . com.calerga.sysquake.SQLinkTool -i 1 -n
fn
x0
dx
n
method

Set the value of x0 to 2:

% java -classpath . com.calerga.sysquake.SQLinkTool -i 1 -s x0 2

SysqualeLink package

The SysquakeLink Java package is installed with Sysquake, in the Java directory on Windows and Linux and inside the Sysquake package on Macintosh. The complete SysquakeLink package name is com.calerga.sysquake.SysquakeLink.

Here are the main design decisions of SysquakeLink:

The reference documentation of SysquakeLink has been generated from source code with the Java utility javadoc. The source code of SQLinkTool and the example below illustrate how to use it.

Here is the list of methods:

connect()
Connect to Sysquake, which must already be running.
disconnect()
Disconnect from Sysquake.
execute(cmd)
Execute a command as if it was typed in the command window.
isConnected()
Check if the connection with Sysquake has been successfully established.
lmeVariableNames()
Get the name of all variables defined in the context of the Command window.
lmeVariableValue(varSpec)
Get the value of variable(s) in the context of the command window.
maximize()
Maximize Sysquake main window (no effect on all platforms).
minimize()
Minimize Sysquake main window (no effect on all platforms).
open(path)
Open an SQ or SQD file.
reload(instanceId)
Reload an SQ or SQD file.
resetVariableChangeNotification(instanceId)
Reset the variable change notification.
restoreSize()
Restore size of Sysquake main window (no effect on all platforms).
setVariableChangeNotification(instanceId,cmd)
Set a variable change notification for executing LME code.
setVariableChangeNotification(instanceId,listener)
Set a variable change notification (listener's variableChange(instanceID) method is called whenever a variable is changed in Sysquake's spefified instance).
setVariableValue(instanceId,varSpec,val)
Change the value of variable(s) and force a redraw.
show()
Show Sysquake (no effect on all platforms).
variableNames(instanceId)
Get the name of all variables of a given instance.
variableValue(instanceId,varSpec)
Get the value of variable(s) for a given instance.
version()
Get the version of Sysquake.

Example

To illustrate how SysquakeLink can be used to exchange information in both directions between Sysquake and a Java application, here is an example where a Java slider is synchronized with the x0 position of the SQ file approx.sq. The application is kept as simple as possible; approx.sq should be opened in Sysquake by the user as the first instance.

Here is the complete source code of the application:

// SysquakeLink packages
import com.calerga.sysquake.*;

// Java and Swing packages
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

// a single class is defined for the whole application
public class SQLinkApproxSample
  extends WindowAdapter      // class used as window listener and...
  implements ChangeListener, // ...slider listener and...
    SQLinkVariableListener   // ...Sysquake notifications listener
{
  JSlider slider;            // java slider
  static final int sqID = 1; // fixed SQ instance ID
  
  public SQLinkApproxSample()
  {
    // application setup
    slider = new JSlider(JSlider.HORIZONTAL, -5000, 5000, 0);
      // JSlider has integer values, x0 = 0.001 * (java value)
    slider.addChangeListener(this);  // using this.stateChanged()

    JPanel panel = new JPanel(new GridLayout(0, 1));
    panel.add(slider);

    JFrame frame = new JFrame("Sync. with approx.sq");
    frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    frame.addWindowListener(this);  // using this.windowClosing()
    frame.setContentPane(panel);
    frame.pack();
    frame.setVisible(true);

    try
    {
      SysquakeLink.connect();
      SysquakeLink.setVariableChangeNotification(sqID, this);
        // using this.variableChange()
      SysquakeLink.show();
        // bring Sysquake to front on platforms which support it
    }
    catch (SQLinkException e)
    {
      System.err.println(e);
      System.exit(1);  // abort
    }
  }
  
  public void windowClosing(WindowEvent e)
  {
    // called when the window is closed
    SysquakeLink.disconnect();
    System.exit(0);
  }
  
  public void stateChanged(ChangeEvent event)
  {
    // called when the Java slider is changed
    // -> update Sysquake's x0
    try
    {
      if (event.getSource() == slider)
      {
        double x0 = slider.getValue() * 0.001;
        SysquakeLink.setVariableValue(sqID, "x0", x0);
      }
    }
    catch (SQLinkException e)
    {
      System.err.println(e);
    }
  }

  public void variableChange(int instanceId)
  {
    // called when the Sysquake's x0 is changed
    // -> update Java slider
    try
    {
      Object obj = SysquakeLink.variableValue(sqID, "x0");
      double x0 = ((Double) obj).doubleValue();
      slider.setValue((int)(1000 * x0));
    }
    catch (Exception e)
    {
      // catch communication and conversion to double errors
      System.err.println(e);
    }
  }

  public static void main(String[] args)
  {
    // application entry point: create instance
    new SQLinkApproxSample();
  }
}