Much of this information is internal documentation. Care has not been made to make links work and make the documentation conprehensible for outsiders.

Communication Architecture

There are three basic entities in the architecture, namely a controller process, an ML-daemon process, and an ML-engine process. The controller sends commands to the ML-daemon or the ML-engine typically based on commands invoked by a user via a user interface. The controller also collects responses from the ML-daemon and ML-engine. It is the responsibility of the user interface to present the responses in an appropriate form for the user. The ML-daemon is a small process which permanently listens on a (socket-based) port. It can start/stop an ML-engine. The ML-engine is a large process which contains all facilities for syntax checking, compiling, and simulation of CP-nets. This process has only one thread, and can be stopped by means of the Parent process.

Communication architecture
The figure above depicts the scenario where a session with the ML-engine is in effect. First the controller asks the ML-daemon to start a new session (1). The ML-daemon starts the needed processes and sets up socket communication which results in the communication architecture annotated with (2). Via the njsmld socket the controller can ask the parent process to stop the ML-engine process. Via the dmo socket the controller can issue commands such as “check a declaration”. Via the dmoEval socket the controller read evaluation feedback from the ML-engine. Note that this socket is mapped directly to stdin/out/error of the ML-engine.

I addition, starting with CPN Tools 4.0, an optional simulator extension daemon is added. It can observe and extend regular simulator commands as well as add new commands in a separate name space. The extension daemon supports 5 communication modes: filtering, invocation, GFC, extension, forward GFC. The five modes are summarized in the MSC below:

The first mode, filtering allows the extensions to subscribe to standard messages. If a subscribe message is sent from the controller to the simulator on the dmo, it is evaluated normally, and the original message along with the response computed by the simulator is packaged in a forward package (See below) and forwarded to the extension daemon. The extension daemon can either acknowledge the receipt or send a modified response.

The second mode, invocation, allows the extension daemon to send regular dmo packages to the simulator. These may be any standard dmo package and are treated as normal packages on the dmo, except the response is sent to the extension server instead of the controller.

The third mode, GFC, works like regular GFC (see below). Note that the CPN Tools GUI implements the format wrongly whereas the extension server implements the formal described in this document. This makes it possible to invoke methods in Java from SML and is useful for making simple RPC stubs.

The fourth mode, extension, works as normal dmo from the GUI; the only difference being that packages are unconditionally forwarded to the extension server. In the future, certain calls may be internalized for efficiency.

The fifth mode, GFC forward, is the dual of the extension mode, and forwards GFC packages from the extension server to the controller. The GFC packages exchanged between the extension daemon and the simulator use the correct format, whereas the packages between the simulator and the controller are in CPN Tools' misunderstood format. Forwarded responses have a different opcode and the original opcode embedded in the message to distinguish responses (opcode 5) from GFC invocations (also opcode 5).

Message Formats

This section documents the various message formats used in order to exchange data with the ML-engine.

Data on the njsmld socket

Once in a session, the parent process will listen to this socket. If the controller exits, the socket will close. This will be discovered by the parent process which subsequently will kill the ML-engine. See also protocol section below. A few opcodes are understood:

GRAM_NJSMLD_Interrupt (opcode=4)

Send SIGINT (interrupt) to the ML-engine. A NJSMLD_GRAM_NoErr (1) is sent back on socket.

This is useful to terminate the ML-engine process gracefully.

GRAM_NJSMLD_UserRequest1 (opcode=5)

Send SIGUSR1 (User Signal 1) to the ML-engine. A NJSMLD_GRAM_NoErr (1) is sent back on socket.

This is useful if you wish to get the simulator gracefully out of an infinite run. It has only effect while the simulator is in a run.

GRAM_NJSMLD_UserRequest2 (opcode=6)

Send SIGUSR2 (User Signal 2) to the ML-engine. A NJSMLD_GRAM_NoErr (1) is sent back on socket.

This signal is currently ignored by the ML-engine.

Data on the dmoEval socket

The streams stdin, stdout, and stderr on the ML-engine are all mapped to the dmoEval socket. Thus feedback from the ML-engine is read on the dmoEval socket. It is however unsafe to send data to the dmoEval during a session. For a description on how to evaluate ML source code see section on opcode 1 below.

Data on the dmo socket

During a session the ML-engine is running in a speciel event listener loop, where messages to the dmo socket are dispatched.

Basic message structure
The basic message format is depicted above. All messages sent to the dmo socket during a session must conform to this format.

Aug. 2007 (wells): Is it correct that all messages have the above format? In the ML code it doesn't seem that the length of the messages is ever read from the stream.

  • - Evaluating ML source (dmo socket, opcode 1) —

ML source code can be sent to the ML-engine for evaluation. The ML-engine then evaluates the source code and feedback must be read on the dmoEval socket. A feedback block is terminated with chr(1).

In case an exception was raised, then the feedback is chr(exception code) + chr(2) instead.

  • - OA result (dmo socket, opcode 2) —

Not documented. Not supported in CPN2000 yet.

  • - Call-back (dmo socket, opcode 3) —

The structure of these messages will be similar to the CPN messages described below for opcode 9, i.e. after the opcode in the message, there will be three integers indicating the number of booleans, integers, and strings in the remaining part of the message, followed by a list of booleans, a list of integers, and a list of strings (each list with the previously specified length). The first integer in the list of integers specifies a particular command that should be executed in the controller. The commands are described below.

In contrast to the messages with opcode 9, these messages are sent by the ML engine, and the GUI/controller sends a reply.

Return values sent back from the controller have opcode=3.

Regular commands are described on the Callback Messages page, and messages related to extensions on theCPN extension messages page.

  • - GFC request and result (dmo socket, opcode 5) —
  • - Chart result (dmo socket, opcode 7) —

Not documented. Not supported in CPN2000 yet.

  • - Controlling the CPN compiler and simulator (dmo socket, opcode 9) —

CPN message structure
The structure of a CPN message is depicted above. The opcode is 9 and the body of the message is divided into a count specification and a list of booleans, list of integers, and list of strings (null-terminated). The first count specification denotes the number of booleans in the list, the second denotes the number of integers, and the third denotes the number of strings. Each string is padded with 0-bytes such that the length of the string block is dividable by 4, however the length prefixing each string block never counts the padding.

Note that the first integer in the list of integers denotes a command. These commands are described below.

Return values sent back have opcode=7 and a command which is 1, 2 or -1. Most often the command is 1, indicating termination of the most recent request, unless an internal error occurs, in which case the command is -1 and the string list contains some diagnostics. An internal error is fatal. Note that command is 1 even in the case of syntax errors. The command 2 indicates that an intermediate response is being sent, after which the simulator will expect a reply from the GUI. When an intermediate response is sent, a response kind will also be sent. For example, a intermediate response is sent from the simulator to the GUI when the user can manually bind a transition (see command 500, subcmd 15 under CPN messages simulate cmd).

  • - Forwarded simulator extension (dmo socket, opcode 10) —

These packets are sent from the simulator to the extension server. They comprise an opcode 9 packet and an opcode 7 response from the simulator. The extension server responds with opcode 7 and command 0 (just forward already known response) or command -1, 1, 2, forward this response instead.

Given a request package (opcode 9):

Extra call parameters:
  blist= b1, b2, ..., ba
  ilist= i1, i2, ..., ib
  slist= s1, s2, ..., sc

and a corresponding response (opcode 7):

Extra call parameters:
  blist= b1, b2, ..., bx
  ilist= i1, i2, ..., iy
  slist= s1, s2, ..., sz

the corresponding forward package looks like this (opcode 10):

Extra call parameters:
  blist= b1, b2, ..., ba, b1, b2, ..., bx
  ilist= a, b, c, x, y, z, i1, i2, ..., ib, i1, i2, ..., iy
  slist= s1, s2, ..., sc, s1, s2, ..., sz
  • - Forwarded GFC response (dmo socket, opcode 11) —

These are renumbered GFC response packets.

Given a GFC response packet (opcode 5/6):

Extra call parameters:
  blist= b1, b2, ..., ba
  ilist= i1, i2, ..., ib
  slist= s1, s2, ..., sc

the corresponding forward GFC response package looks like this (opcode 11):

Extra call parameters:
  blist= b1, b2, ..., ba
  ilist= 5/6, a, b, c, i1, i2, ..., ib
  slist= s1, s2, ..., sc

Typical Usage of ML-engine

The typical sequence of commands is:

  1. bootstrap (cmd=100)
  2. init syntax check (cmd=400, subcmd=1)
  3. init code generation (cmd=400, subcmd=5)
  4. compile declarations (cmd=300, subcmd= … )
  5. syntax check diagram (cmd=400, subcmd= … )
    1. check pages recursively (check subpage first)
  6. code generate instances
    1. create instances (cmd=500, subcmd=1)
    2. set simulation options (cmd=200, subcmd=10)
    3. set initialisation simulation options (cmd=200, subcmd=11)
    4. init simulator (cmd=500, subcmd=21)
  7. check transition enabledness (cmd=500, subcmd=35)
  8. get markings (cmd=500, subcmd=31)
  9. simulate
    1. start simulation run (cmd=500, subcmd=11)

Detailed descriptions of the formats of CPN messages can be found on the following pages:

CPN Message Formats


This section documents the most important protocols used for controlling the ML-engine process.

Start a new session with ML-engine

New session with ML-engine
The figure above depicts the protocol used in order to start a new session with the ML-engine, i.e., the protocol that must be used in order to start the ML-engine process. The protocol is divided into several stages as described in the following:

Stage 1 (create sockets): The three sockets njsmld, dmoEval, and dmo are created. After each socket is created, trivial data is sent and received in order to test the sockets.

Stage 2 (create parent process): The parent process is created. The temp process is used in order to detach the parent process completely from the ML-daemon. The ML-deamon is now ready for new connections again.

Stage 3 (login session): Send login information which is verified by the parent process. If successful the parent process changes userid such that subsequent actions happens with the permission of the login user.

Stage 4 (create ML-engine): The ML-engine process is created and immediately executes the ML-image based on the path information given by the controller. The ML-engine process inherits all open socket connections, but closes the njsmld socket. The parent process closes the dmo and dmoEval sockets.

Stage 5 (ML startup): Once the ML-system starts it prints a version banner on stdout (terminated with chr(1)), which is parsed and verified by the controller.

Stage 6 (ML-engine ready): The ML-engine is now in an event loop and is ready to dispatch messages read on the dmo socket. The parent process listens for messages on the njsmld socket.

In-session example: Start a simulation run

Start a simulation run in ML-engine
The figure above depicts the protocol used in order to start a simulation run in the ML-engine.

Terminate ML-engine

Terminate the ML-engine
The figure above depicts the protocol used in order to terminate the ML-process in a graceful manner.