Project

General

Profile

ACNET API for Erlang

The acnet application contains several modules containing ACNET-related data types and functions that communicate with acnetd.

acnet Module

The acnet contains the main, high-level calls needed to communicate via ACNET.

Data Types

acnet:status() :: integer().

Represents a 16 bit, ACNET status.

acnet:taskname() :: string().

The name of an ACNET task. A task name contains up to 6 alphanumeric characters.

acnet:task_node_s() :: string().

A network address of an ACNET task represented as an Internet-style name (e.g. "TASK@NODE".)

acnet:trunknode() :: non_neg_integer().

A network address of an ACNET node represented as an integer. This form combined the trunk and node addresses into a single integer value.

acnet:task_node_p() :: {taskname(), trunknode()}.

A network address of an ACNET task given as a task-name/trunk-node pair.

acnet:address() :: task_node_s() | task_node_p().

A network address of an ACNET task. Functions accepting this type are indicating they accept either forms of task addressing.

acnet:unique_request_id().

A value representing an active request a client created. Once the associated request ends, this value becomes permanently invalid.

acnet:unique_reply_id().

A value used to route replies to an incoming request.


acnet:accept_requests

acnet:accept_requests(Svr :: atom())
acnet:accept_requests(Svr :: atom(), Proto :: module()) ->
    'ok' |
   {'error', acnet:status()}.

Registers the calling process as the receiver of ACNET requests and USMs. The parameter Svr indicates the ACNET connection to which the process is attaching. Incoming requests, USMs, and cancels are sent to the process' message queue. The messages have the following form:

-record(acnet_request, {ref : term(), mult : bool(), data = binary() | Proto:request()}).
-record(acnet_usm, {data : binary() | Proto:request()}).
-record(acnet_cancel, {ref : term()})

When using the first form, accept_requests/1, the data field in the incoming message simply contains a binary value containing the ACNET payload. In the second form, the Proto parameter is an atom representing a module name that follows the protocol behavior. The ACNET process will call Proto:unmarshal_requests/1, which converts a binary to an Erlang term. The term will then appear in the data field of the message.

The mult field will be true or false, depending whether the request wants, respectively, multiple replies or not.

The ref field uniquely identifies the incoming request. It is passed as the first parameter to acnet:send_reply/2,3 and acnet:send_last_reply/2,3 to send replies associated with the request.


acnet:cancel_request

acnet:cancel_request(Ref :: acnet:unique_request_id()) ->
    'ok' |
   {'error', acnet:status()}.

Cancels a previously created request. The Ref parameter is a value returned by the request_reply/request_replies functions.


acnet:check_task

acnet:check_task(Svr :: atom(), Task :: acnet:taskname()) ->
    'ok' |
   {'error', acnet:status()}.

Queries a remote ACNET node to see if the specified task is currently registered. This function returns ?ACNET_SUCCESS if the task exists. Otherwise, ?ACNET_BADTASK is returned if it doesn't exist, ?ACNET_BADNODE is the node address is invalid, or ?ACNET_UTIME if a timeout occurs. It should be noted that, even if this function returns success, the remote task may exit at any time.


acnet:get_addr

acnet:get_addr(Svr :: atom(), Name :: string()) ->
    {'ok', integer()} |
    {'error', acnet:status()}.

Looks up the trunk/node address of a node with the name Name. The return value is a 2-tuple of the form {ok, Addr} , where Addr is an integer, if the lookup was successful or {error, Status}, where Status is an ACNET error status, if an error occurred.


acnet:get_local_node

acnet:get_local_node(Svr :: atom()) ->
    {'ok', acnet:address()} |
    {'error', acnet:status()}.

Returns the trunk/node address of the local node. ACNET nodes can have multiple trunk/node pairs. When an ACNET connection is made, one of the aliases must be chosen. The address returned by this function represents the trunk/node associated with the connection used.


acnet:get_local_node_name

acnet:get_local_node(Svr :: atom()) ->
    {'ok', integer()} |
    {'error', acnet:status()}.

Returns the node name of the local node (as a Rad50 integer.) ACNET nodes can have multiple trunk/node pairs. When an ACNET connection is made, one of the aliases must be chosen. The name returned by this function represents the name associated with the connection used.


acnet:get_name

acnet:get_name(Svr :: atom(), Addr :: trunknode()) ->
    {'ok', string()} |
    {'error', acnet:status()}.

Looks up the name of a node with the trunk/node address Addr. The return value is a 2-tuple of the form {ok, Name} , where Name is a string, if the lookup was successful or {error, Status}, where Status is an ACNET error status, if an error occurred.


acnet:get_state

acnet:get_name(Svr :: atom()) ->
    term().

Returns the internal state associated with the ACNET connection. This format is not documented, as it may change between versions of the application. This is a diagnostic/debugging tool, at best. Operational code should not rely on this function.


acnet:ignore_request

acnet:ignore_request(ReplyID :: acnet:unique_reply_id()) ->
    'ok' |
   {'error', acnet:status()}.

Returns the internal state associated with the ACNET connection. This format is not documented, as it may change between versions of the application. This is a diagnostic/debugging tool, at best. Operational code should not rely on this function.


acnet:report_connections

acnet:report_connections(Svr :: atom()) ->
    'ok'.

Prints a table of active connections used by the ACNET process. This is useful in the Erlang shell, but not so much for programs.


acnet:request_reply, acnet:request_replies

acnet:request_reply(Svr :: atom(), Dst :: acnet:address(), Pkt :: binary())
acnet:request_reply(Svr :: atom(), Dst :: acnet:address(), Pkt :: Proto:request(), Proto :: module())
acnet:request_replies(Svr :: atom(), Dst :: acnet:address(), Pkt :: binary())
acnet:request_replies(Svr :: atom(), Dst :: acnet:address(), Pkt :: Proto:request(), Proto :: module()) ->
    acnet:unique_request_id().

Sends a request to a remote ACNET task: request_reply/3,4 sends a request that expects one reply; request_replies/3,4 sends a request that expects multiple replies. Dst specifies the recipient of the request and can take one of two forms. The first form is as a string: "TASK@NODE". The other is as an integer pair where the first element is the RAD50 representation of the task name and the second element is the trunk/node. Pkt is the request data to be sent. For the functions without the Proto parameter, Pkt is a binary which is sent as-is. The functions that take a Proto parameter expect Pkt to be an Erlang term that will be converted to a binary using the functions in the Proto module. Replies to this request will also be converted to Erlang terms by this module. If the message cannot be marshaled by the module, an exception will be thrown.

This function returns a value that identifies the request. It can be passed to acnet:cancel_request/2 to close out the request.


acnet:rpc

acnet:rpc(Svr :: atom(), Dst :: acnet:address(), Tmo :: 'infinity' | integer(), Data :: binary())
acnet:rpc(Svr :: atom(), Dst :: acnet:address(), Tmo :: 'infinity' | integer(), Data :: PMod:request(), PMod :: module()) ->
    {acnet:status(), binary() | term()}.

Performs a remote procedure call. This function sends the Data parameter as the request payload (the optional PMod parameter can be used to specify a protocol marshaller/unmarshaller module.) The return value will be a pair containing the ACNET return status and the reply data -- {Sts, Data} . If Tmo milliseconds elapses before a reply is received, then {?ACNET_UTIME, <<>>} is returned.

It should be noted that the processing of other messages in a process' queue will be delayed while it is waiting for a reply. To keep the system responsive, a process should be spawned which only performs the rpc and sends the result to the caller's queue.


acnet:rpc_mult

acnet:rpc_mult(Svr :: atom(), Dst :: acnet:adress(), Tmo :: non_neg_integer(), Max :: non_neg_integer(), Data :: binary())
acnet:rpc_mult(Svr :: atom(), Dst :: acnet:adress(), Tmo :: non_neg_integer(), Max :: non_neg_integer(), Data :: PMod:request(), PMod :: module()) ->
    list({acnet:status(), binary() | PMod:reply()}).

Performs a remote procedure call in which a list of results is expected. This function sends the Data parameter as the request payload (the optional PMod parameter can be used to specify a protocol marshaller/unmarshaller module.) The return value will be a list of pairs, each containing the ACNET return status and the reply data -- {Sts, Data} . If Tmo milliseconds elapses before a reply is received, then the last item in the list will be {?ACNET_UTIME, <<>>}. The Max parameter indicates the maximum number of items to return. If the remote task tries to send more, the results will be clipped and the last entry will contain {?ACNET_TRUNCREPLY, <<>>}.

It should be noted that the processing of other messages in a process' queue will be delayed while it is receiving and assembling the replies. To keep the system responsive, a process should be spawned which only performs the rpc and sends the result to the caller's queue.


acnet:send_last_reply, acnet:send_reply

acnet:send_last_reply(Ref :: acnet:unique_request_id(), Data :: term() | binary())
acnet:send_last_reply(Ref :: acnet:unique_request_id(), Sts :: acnet:status(), Data :: term() | binary())
acnet:send_reply(Ref :: acnet:unique_request_id(), Data :: term() | binary())
acnet:send_reply(Ref :: acnet:unique_request_id(), Sts :: acnet:status(), Data :: term() | binary()) ->
    acnet:status().

Sends the reply packet, Data, to the request associated with Ref. The format of Data depends on how the process was attached to ACNET (via the accept_requests/1,2 functions.) If a protocol module was specified, then Data is an reply type from that protocol which will be marshaled to a binary. If no protocol module was specified, then Data should just be a binary value.

The "last reply" versions will close out a request for multiple replies (i.e. Ref will be invalid after the call.) The Sts parameter can be used to specify an ACNET error status.

If the request was for a single reply, send_reply/2,3 will not keep the request open -- ACNET will convert the reply to a "last reply" automatically.

If a reply is sent and the request is still open, ?ACNET_SUCCESS is returned. If reply is sent that also closes the request, ?ACNET_ENDMULT is returned. If Ref is invalid, ?ACNET_NOSUCH is returned. If the message couldn't be marshaled, ?ACNET_BADRPY is returned.


acnet:send_usm

acnet:send_usm(Svr :: atom(), Dst :: acnet:address(), Pkt :: binary())
acnet:send_usm(Svr :: atom(), Dst :: acnet:address(), Pkt :: Proto:request(), Proto :: module()) ->
    acnet:status().

Sends a datagram to a remote ACNET task. Dst specifies the recipient of the request and can take one of two forms. The first form is as a string: "TASK@NODE". The other is as an integer pair where the first element is the RAD50 representation of the task name. The second element is the trunk/node. Pkt is the request data to be sent. For the functions without the Proto parameter, Pkt is a binary which is sent as-is. The functions that take a Proto parameter expect Pkt to be a request type from the protocol that will be converted to a binary using the functions in the Proto module.


acnet:set_info

acnet:set_info(ReplyID :: acnet:unique_reply_id(), Info :: any()) ->
    'ok' |
   {'error', acnet:status()}.

Associates information with a reply ID. This information will be visible when calling acnet:report_connections/1 and acnet:get_state/1.


acnet:start

acnet:start(Svr :: atom(), Name :: string()) ->
    'ok'.

Starts a new process that manages an ACNET connection. The process can be referred to through the specified registered name, Svr. The process will attempt to connect with ACNET using the name, Name.


acnet:stop

acnet:stop(Svr :: atom()) ->
    'ok'.

Shuts down the ACNET application. All connected clients are disconnected.


acnet:spawn_link_replier

acnet:spawn_link_replier(RpyID :: unique_reply_id(), F :: fun((unique_reply_id()) -> no_return())) ->
    acnet:status().

Same as acnet:spawn_replier/2 except the spawned process is linked atomically to the calling process.


acnet:spawn_replier

acnet:spawn_replier(RpyID :: unique_reply_id(), F :: fun((unique_reply_id()) -> no_return())) ->
    acnet:status().

Spawns a process and transfers ownership of the reply ID to it, atomically. RpyID is the reply ID obtained from an ACNET request message. F is a function in which the spawned process will run. Its only parameter is the reply ID it owns.


acnet:transfer_request

acnet:transfer_request(RpyID :: acnet:unique_reply_id(), Pid :: pid()) ->
    acnet:status().

Each request ID and reply ID is considered to be "owned" by one Erlang process. By default, reply IDs are owned by the process accepting requests on the handle. A typical design pattern is to hand the request off to a spawned process. If this is desired, then the ACNET process needs to adjust its knowledge as to the owner (ACNET only allows the owner of a request to reply to it and, if the owner terminates abnormally, closes the request to the remote requester with an error status.)

It should be noted that this function still requires the caller to somehow pass the reply ID to the process handling it. It also doesn't offer protection of the handler process trying to use the reply ID before ownership was transferred. A better solution to spawning these subprocesses is to use either acnet:spawn_replier/2 or acnet:spawn_link_replier/2.

status Module

The status module contains routines to convert ACNET status codes into integers. It also has helper functions that indicate the severity of an ACNET error code.


status:error_code

status:error_code(Code :: acnet:status()) ->
    integer().

Returns an integer representing the error code contained in the status.


status:facility_code

status:facility_code(Code :: acnet:status()) ->
    integer().

Returns an integer representing the facility code contained in the status.


status:from_int

status:from_int(Code :: integer()) ->
    acnet:status().

Converts an integer Code into the internal representation of an ACNET status code (the internal type is exported from the acnet module as acnet:status().


status:is_fatal

status:is_fatal(Code :: acnet:status()) ->
    boolean().

Returns true if the status, Code, represents a fatal error.


status:is_warning

status:is_warning(Code :: acnet:status()) ->
    boolean().

Returns true if the status, Code, has a positive, non-zero error code.


status:to_int

status:to_int(Code :: acnet:status()) ->
    integer().

Converts an ACNET status type into an integer.

protocol BEHAVIOR

A library that supports a given protocol needs to implement the protocol behavior. The Erlang generator of the protocol compiler creates Erlang modules that implement this behavior.


MOD:marshal_request(X)

Converts the request term, X, into a binary.


MOD:unmarshal_request(X)

Converts a binary, X, into a decoded request term defined by the protocol.


MOD:marshal_reply(X)

Converts the reply term, X, into a binary.


MOD:unmarshal_reply(X)

Converts a binary, X, into a decoded reply term defined by the protocol.

Examples

ACNET communications is asymetric, so whether you're writing a client application that makes requests to an ACNET service or making an ACNET service determines which subset of the API you'll be using.

Client

For the client examples, it is assumed that acnet:start(client, "CLIENT") has been called. This connects the Erlang VM to ACNET with the name "CLIENT". It also registers the ACNET process with the local name client so local Erlang processes can send it messages.

Sending a USM

This sends a binary packet as a USM payload to a remote task.

acnet:send_usm(client, "TASK@NODE", <<your binary data>>),

Making a "One-Shot" Request

This example sends a request that expects a single reply, performs some stuff and then cancels the request.

Reqid = acnet:request_reply(client, "TASK@NODE", <<your binary data>>),
...
acnet:cancel_request(Reqid),

Replies to a request are delivered to the process' message queue in the form

#acnet_reply{ref=acnet:unique_request_id(), src={int(), int()}, last=bool(), status=acnet:status(), data=term()} .

For instance, you could make a request and wait for the response by doing this:

Reqid = acnet:request_reply(client, "TASK@NODE", <<your binary data>>),
Data = receive
          #acnet_reply{ref=Reqid, data=D} -> D
       end,