This guide details the core concepts used in the Pulsonix Scripting API and how to interact with the API. This guide is language agnostic, which means that it is not explained in terms of a programming language or client implementation.
To use the scripting API, you must first set up Pulsonix to serve API requests via the Scripting Dashboard.
This guide is primarily for users wanting to implement custom scripting clients. Most users will not need to do this to access scripting, and can instead use the Pulsonix Python client.
Key Concepts
Clients, servers and sessions:
A scripting client is any application which connects to a running Pulsonix application, which acts as the server. Connecting to the server begins a new session which has a state that persists until the connection is terminated.
Connections:
Client-server connections are established across Windows Named Pipes, an inter-process communication mechanism. Connections are terminated when the client disconnects from the pipe or when the server closes the pipe.
Objects:
Communication with the API is done in terms of objects of particular classes. Each object in a scripting session is identified with a unique numeric ID and has a class type. Objects in the API is a effectively a handle to an object managed by the scripting server, such as a design or component - they do not contain any data of their own except for their ID.
Classes:
The API is composed of multiple classes (classifications of objects), which are based on classes in object-oriented programming languages. Classes are composed of properties, methods and enumerations.
- Properties, referred to as “attributes”, “keys” or “member values” in some programming languages, are characteristics about an object of a particular class which can be retrieved, changed or both (“get”, “set” or both).
- Methods, referred to as “functions” or “methods” in some programming languages, are actions on an object of a particular class which can be “called”, “run” or “invoked”. Methods are called with a list of arguments and produce a return value.
- Enumerations are sets of named integers which classes can define to give meaning to some of their properties or method parameters.
Like in many object-oriented programming languages, a class can inherit from another, which means that it inherits the properties and methods of the base class and can be used in the same way as the base class. Another term for “inherits” is “derives”.
Types:
Values given as parameters to or returned from scripting facilities have specific types, which can be primitive types, enumeration types, class types, array types or variant types.
Primitive types:
The Pulsonix API defines on the following primitive types:
none- A type of one single value, usually denoting the absence of a real result.bool- A type of two values, either “true” or “false”.int- A 64 bit signed integer type.float- A double precision floating point number type.str- A string of unicode text characters in UTF-8.Point- A pair of 32 bit signed integer values representing a 2D coordinate.Rectangle- An axis-aligned box defined by two points, usually representing a 2D area.Dictionary- An unordered collection of pairs unique string keys and string values.
Class types:
Any of the classes defined by the API can serve as a type, which is referred to by the class’ name.
Enumeration types:
Each enumeration defined by an API class can serve as a type, which is referred to in the form
ClassName.EnumerationName. Only values defined by an enumeration are accepted by the API as values
of that enumeration, unless the enumeration is designated as a bitfield, in which case any bitwise
combination of values is permitted.
Atomic types:
Primitive, enumeration and class types are referred to as atomic types.
Array types:
An array type, known as a “list” or “vector” in some programming languages, is a type which is an ordered and
dynamically-sized collection of values of one atomic type. Arrays are denoted as Array[T], where
T is some other atomic type.
Variant types:
A variant type, known as a “union” in some programming languages, is a type which can take the value of a small
set of other atomic or array types. Variants are denoted as Variant[Ts...], where Ts...
is a non-empty comma separated list of atomic or array types which the variant supports.
The type Variant[T, none] is also called an optional type.
Compound types:
Array types and variant types referred to as compound types.
The ScriptObject class:
This is the class which all other API classes derive from, and has basic methods for accessing type and lifetime.
The DesignItem class:
This is the base class for all classes which provide access to an item on a Pulsonix document, such as a design, component, track or pad. The scripting server will automatically discard DesignItem objects tied to design elements which have been deleted.
Helper objects:
Helper objects are instances of a class which derives from ScriptObject but not DesignItem. As such, they are only deleted on request.
The Application class and object:
The ID of 0 is reserved for an Application object which is created as soon as a scripting session begins. This object continues to exist until the end of the session, and is the main interface to other parts of the API.
Requests:
A scripting client makes requests to a server, which are usually of the forms “Call [method name] on [object ID] with [argument list]”, “Get [property name] on [object ID]” or “Set [property name] on [object ID] to [value]“.
Responses:
Requests are always answered to with a response from the server. They can contain requested information, signify that an API call was completed or contain an error message.
Errors:
Responses may contain error messages instead of an expected value, which generally indicate misuse of API functions.
Fatal errors:
Responses may contain “fatal” errors, which generally indicate an unexpected scripting server error. Once a fatal error is sent, the scripting session is terminated by the server.
Establishing sessions
When a scripting server is running, a Windows Named Pipe is opened at \\.\pipe\PulsonixScripting.
Connecting to this pipe as a client allows you to make scripting requests immediately. The buffer size of the
pipe is 1MiB (1024 KiB, or 1048576 bytes), which is a limit on the size of messages which can be sent.
Messaging
Once a connection is established, requests can be sent to the named pipe and responses can be read from it. Requests and responses are encoded in JavaScript Object Notation (JSON) format. Requests for method calls, property reads and property writes are encoded as follows:
{"func": <function name>, "id": <object ID>, "args": <array of argument values>}
{"get": <property name>, "id": <object ID>}
{"set": <property name>, "id": <object ID>, "value": <value>}
The id field in each message is optional - if it is not present, the ID of the Application object
is used.
Responses are either “results” or “errors”, which are encoded as follows:
{"val": <return value>}
{"err": <descriptive string>, "type": <"fatal" or "nonfatal">}
Non-fatal errors messages will also contain a “code” field with an integer that identifies error types.
Note that API types do not have to be specified in requests or responses - they are deduced by the JSON type and the method or property being accessed. Every API type has conversions to and from JSON types:
| API Type | JSON Encoding |
|---|---|
none | null |
bool | bool |
int | number |
float | number |
str | string |
Point | array containing two numbers |
Rectangle | array containing four numbers |
| Class Type | number representing the object’s ID |
| Enumeration Type | number |
Dictionary | A JSON object with only string values |
Array[T] | array of JSON encoded Ts |
Variant[Ts...] | One of Ts... encoded in JSON |
Variants never support types that have ambiguous interpretations, so it’s always clear which T is
encoded in them.
Protocol
Scripting sessions begin when a client successfully connects to Pulsonix via named pipe. Clients make requests by writing one request at a time to the pipe, after which they are expected to wait for a response from the server. Once a response is removed from the pipe, another request can be made.
The scripting session may be ended at any point by the client disconnecting from the pipe. It may also be closed by the server in the event of a fatal error - if a fatal error message is received by the client, any calls made for the remainder of the session will fail.
Each instance of Pulsonix will open an additional instance of the named pipe, which in turn can be connected to by an additional client.
Any language with access to the Windows Named Pipe system can interact with the scripting API. The Python client can be used as reference for how to make API calls in other languages.
Getting API information
User-oriented API documentation is available in the Scripting Reference.
A request containing an empty JSON object ({}) will be responded with a string encoding a JSON object
that contains information about available API functionality, including a list of classes, properties, methods,
enumerations and non-fatal error codes.
The information given can be used to enable custom scripting clients which are always up to date with the functionality supported by the API.
Using the Pulsonix Python client
The Python client generated by the Scripting Dashboard can be used to create a custom client in a few ways:
- The
psxutils.pyfile can be used as reference for how API communication is done over named pipes. - The client functionality can be used directly by custom clients which wrap over it.
- The
get_api_datafunction inpsxutils.pycan be used to get API information for generating other clients.