Project

General

Profile

ACNET Connectivity for Javascript

The documentation for the v1.x version of the API has been moved (Javascript API v1.x). All new code should use this v2.x API.

A Javascript module is available to create ACNET client1 applications. The current implementation uses promises to support asynchronous ACNET communication. Using the async/await keywords makes ACNET interactions a breeze! Examples are given below.

Data is passed to and from ACNET in binary form, which is done using Javascript ArrayBuffer objects. Most Javascript applications should use a higher-level library to interface to ACNET services. This API is primarily for Javascript programmers that are writing libraries for ACNET services.

We are working on a packaging system which will automatically include necessary Javascript modules, but it's not ready yet.

Adding ACNET Support

To enable ACNET communications in your web page, specify the ACNET module in your page's header. The following example assumes your page is hosted on www-bd.fnal.gov, so it specifies an absolute path without the host specifier.

<html>
  <body>
    <!-- Body of document. -->

  <script type="module" >
    import { ACNET, Status, AcnetError, Replies } from "./acnet-2.0.js";

    // Do things with ACNET variable.
  </script>
  </body>
</html>

API

These are the available functions to communicate using ACNET. The functions and properties are expressed using Typescript syntax.


Interfaces

The ACNET module uses the following Typescript interfaces.

Reply<T>

Defines the generic form of messages returned by ACNET. All messages contain two or three fields:

"status" holds an instance of Status and indicates the success/failure of the request.

"sender" holds the trunk/node address of the replier.

"msg", if present, holds a message of type T. For low-level ACNET calls (e.g. .oneshot()) the type will be ArrayBuffer. Client code can, however, decode the raw binary into a more meaningful message.


Classes

ACNET Class

constructor

ACNET()

const con = new ACNET()

Creates an ACNET connection object. This object is used to communicate with an instance of acnetd running on a remote node. The Javascript module uses WebSockets to connect to a pool of acnetd listeners. Although it is possible, using this API, to determine which ACNET node your script is using, the information isn't useful since you can't force the API to pick a particular node.

ACNET connections are a limited resource on the remote node so it is good practice, when writing higher level libraries, to allow sharing an ACNET connection.

property

.isConnected: boolean

Returns a boolean value indicating whether the ACNET object is connected to the ACNET Control System. The object tries to remain connected but it is possible that the connection gets broken (if someone restarts the web server, for instance.) This property has limited use since connection status is better tracked using the notifyOnConnect/notifyOnDisconnect functions.

.getNode(name: string): Promise<number>

Performs a node lookup to translate the ACNET node name, name, into a trunk/node address. Returns a promise which gets resolved with the trunk/node address or rejected with an ACNET status code.

// Get the trunk/node for CLX73.

try {
    const addr = await con.getNode("CLX73");

    console.log("CLX73 is ", addr);
}
catch (e) {
    console.warn("error looking up node: ", e);
}

.notifyOnConnect(f: (handle: number) => void): void

Registers the callback, f, to be called whenever the ACNET object successfully connects to the Control System. If the ACNET object is already connected when this function is called, the callback will be called immediately. The callback will receive a single parameter which is the ACNET handle the client received when connecting (Javascript clients don't get to specify their handle.)

The callback will be invoked after all previously registered callbacks complete. There is no way to unregister a callback.

.notifyOnDisconnect(f: () => void): void

Registers the callback, f, to be called whenever the ACNET object loses its connection with the Control System. The callback receives no parameters and will be invoked after all previously registered callbacks complete. There is no way to unregister a callback.

.oneshot(dst: string, obj: Marshalable | ArrayBuffer, tmo: number): Promise<Reply<ArrayBuffer>>

Sends a request expecting a single reply to a remote ACNET task, specified by dst. This function returns a promise which will be resolved or rejected based on the results of the request.

dst is a string using an Internet-like form to specify the task. For instance, to send a request to a task registered as SERVER on CLX73, you would use "SERVER@CLX73".

obj can be an instance of ArrayBuffer that contains binary information to send with the request. If it's an object with a method called marshal(), that method will be used to convert the object's contents to an ArrayBuffer. This method is generated by the protocol compiler, to make it easier to support protocols used in the control system.

tmo is a timeout, in milliseconds. If a reply isn't received within this time, the promise it rejected with an ACNET_UTIME status code.

When the single reply is received, the promise will be resolved with a Reply<ArrayBuffer> object.

.stream(dst: string, obj: Marshalable | ArrayBuffer, tmo: number): Promise<Replies>

Sends a request expecting a stream of replies to a remote ACNET task, specified by dst. The function returns an async iterator which can be used in a for-await-of loop.

dst is a string using an Internet-like form to specify the task. For instance, to send a request to a task registered as SERVER on CLX73, you would use "SERVER@CLX73".

obj can be an instance of ArrayBuffer that contains binary information to send with the request. If it's an object with a method called marshal(), that method will be used to convert the object's contents to an ArrayBuffer. This method is generated by the protocol compiler, to make it easier to support protocols used in the control system.

tmo is a timeout, in milliseconds. If a reply isn't received within this time, the iterator ends with a single value containing an ACNET_UTIME status code.

The iterator lazily returns a stream of Reply<ArrayBuffer> objects.


AcnetError Class

This class extends the Javascript Error class to report ACNET error status. It's more resource-intensive than the Status class, so it should only be used when you want the extra information that the Error class gives (i.e. stack traces.)

constructor

AcnetError(s: Status)

Wraps a Status value with error information.

property

.status: Status

Returns the ACNET status used to build the AcnetError instance.


Replies Class

This class operates as an asynchronous iterator which means each time the .next() method is called, a promise is returned. For this class, the actual type is Promise<Reply<ArrayBuffer>>. Instances of this class are obtained from a call to ACNET.stream() and they allow an application to iterate, asynchronously, over the replies generated by the request.

constructor

Replies(cf: () => Promise<Status>)

Normal applications don't create instances of this class. The proper way to obtain an instance is calling ACNET.stream().

.cancel(): Promise<Status>

Depending on the ACNET service, a stream of replies can be finite or infinite. If an application no longer wants to receive replies, it calls this method to cancel the request and free up the associated resources.

.next(): Promise<{value: Reply; done: boolean}>

Returns a promise which holds, or will hold, the next, sequential reply from the stream.

NOTE: next() should only be used by one consumer. Multiple consumers using the same Replies instance will result in undefined behavior.


Status Class

A class to represent ACNET status codes. If an ACNET status is to be thrown via exception, it should be wrapped with an AcnetError instance so that stack trace information will be included. This class is intended to be lightweight and provide methods to query the underlying representation.

constant

Status.ACNET_DISCONNECT

This status indicates the ACNET object has lost contact with the control system. It will try to reconnect to ACNET every 5 seconds so this status should represent a temporary error state.

constant

Status.ACNET_NONODE

Indicates the target node doesn't exist.

constant

Status.ACNET_SUCCESS

Indicates a successful completion.

constructor

Status(f: number, e?: number)

Builds a Status value from the given "facility code", f, and "error code", e. If the error code isn't provided, f is assumed to be the full, 16-bit status value. It should be noted that there is a set of predefined, valid ACNET status values and building arbitrary status values is frowned upon. If this library doesn't have the status you're looking for, it should be added.

property

.isBad: boolean

Returns true if the status represents an error status.

property

.isGood: boolean

Returns true if the status represents a good status. A good status can be simply Status.ACNET_SUCCESS or it can be a status with a positive error code.

property

.raw: number

Returns the underlying value of the status. This property is useful when building network packets and you need to encode the underlying numeric value of the status. Applications should avoid this property and use the Status object whenever possible.

.toString(): string

Returns a string representation of the status in Fermilab's standard form: "[F E]", where "F" is the facility code and "E" is the error code. For instance, ACNET_DISCONNECT will return "[1 -34]".

.equals(o: Status): boolean

Returns true if the calling object is equal to the status of the o object.


1 Since web pages can be started anywhere, we don't allow Javascript connection to provide ACNET services; they can only be ACNET clients.