O2 2.0
A communication protocol for interactive music and media applications.
Classes | Macros | Typedefs | Enumerations | Functions | Variables
Basics

Classes

struct  O2msg_data
 data part of an O2 message More...
 
struct  O2message
 an O2 message container More...
 
struct  O2blob
 The structure for binary large object. More...
 
union  O2arg
 union of all O2 parameter types More...
 

Macros

#define O2MEM_ALIGN   8
 get the type string from o2_msg_data_ptr More...
 
#define O2MEM_ALIGNUP(s)   ( ((s)+(O2MEM_ALIGN-1)) & ~(O2MEM_ALIGN-1) )
 
#define O2MEM_BIT32_ALIGN_PTR(p)   ((char *) (((size_t) (p)) & ~3))
 
#define O2_MSG_DATA_END(data)   (PTR(&(data)->misc) + (data)->length)
 
#define o2_msg_data_types(data)   (o2_next_o2string((data)->address) + 1)
 
#define o2_msg_types(msg)   o2_msg_data_types(&msg->data)
 
#define o2_msg_data_params(types)    o2_next_o2string((const char *) ((intptr_t) (types) & ~3))
 
#define O2_MSG_PAYLOAD(msg)   PTR(&(msg)->data.misc)
 The address of the actual message, not including the length field:
 
#define o2_send(path, time, ...)
 Construct and send O2 message with best effort protocol. More...
 
#define o2_send_cmd(path, time, ...)
 Construct and send an O2 message reliably. More...
 
#define streql(a, b)   (strcmp((a), (b)) == 0)
 
#define O2_MAX_PROCNAME_LEN   32
 
#define O2_MAX_NAME_LEN   63
 
#define O2_MALLOC(x)   o2_dbg_malloc(x, __FILE__, __LINE__)
 
#define O2_MALLOCNT(n, typ)   ((typ *) O2_MALLOC((n) * sizeof(typ)))
 
#define O2_MALLOCT(typ)   O2_MALLOCNT(1, typ)
 
#define O2_FREE(x)   o2_dbg_free(x, __FILE__, __LINE__)
 
#define O2_CALLOC(n, s)   o2_dbg_calloc(n, s, __FILE__, __LINE__)
 
#define O2_CALLOCNT(n, typ)   ((typ *) O2_CALLOC(n, sizeof(typ)))
 
#define O2_CALLOCT(typ)   O2_CALLOCNT(1, typ)
 
#define O2MEM_DEBUG   1
 

Typedefs

typedef double O2time
 O2 timestamps are doubles representing seconds since the approximate start time of the ensemble.
 
typedef struct O2msg_data O2msg_data
 data part of an O2 message More...
 
typedef struct O2msg_datao2_msg_data_ptr
 
typedef struct O2message O2message
 an O2 message container More...
 
typedef struct O2messageO2message_ptr
 
typedef struct O2blob O2blob
 The structure for binary large object. More...
 
typedef struct O2blobO2blob_ptr
 
typedef enum O2typeO2type_ptr
 
typedef union O2argO2arg_ptr
 
typedef void(* O2method_handler) (const o2_msg_data_ptr msg, const char *types, O2arg_ptr *argv, int argc, const void *user_data)
 callback function to receive an O2 message More...
 
typedef O2time(* o2_time_callback) (void *rock)
 signature for callback that defines the reference clock More...
 

Enumerations

enum  O2type {
  O2_INT32 = 'i' , O2_FLOAT = 'f' , O2_STRING = 's' , O2_BLOB = 'b' ,
  O2_ARRAY_START = '[' , O2_ARRAY_END = ']' , O2_INT64 = 'h' , O2_TIME = 't' ,
  O2_DOUBLE = 'd' , O2_SYMBOL = 'S' , O2_CHAR = 'c' , O2_MIDI = 'm' ,
  O2_TRUE = 'T' , O2_FALSE = 'F' , O2_NIL = 'N' , O2_INFINITUM = 'I' ,
  O2_BOOL = 'B' , O2_VECTOR = 'v'
}
 An enumeration of the O2 message types. More...
 
enum  O2tap_send_mode { TAP_KEEP , TAP_RELIABLE , TAP_BEST_EFFORT }
 

Functions

O2err o2_internet_enable (bool enable)
 Disable Internet connections. More...
 
O2err o2_network_enable (bool enable)
 Disable external nework connections. More...
 
const char * o2_next_o2string (const char *str)
 
void o2_message_print (O2message_ptr msg)
 print an O2 message to stdout More...
 
void o2_msg_data_print (o2_msg_data_ptr msg)
 print a message from msg_data_ptr to stdout More...
 
O2err o2_initialize (const char *ensemble_name)
 Start O2. More...
 
int o2_version (char *version)
 get O2 version number More...
 
int o2_memory (void *((*malloc)(size_t size)), void((*free)(void *)), char *first_chunk, int64_t size, bool mallocp)
 Tell O2 how to allocate/free memory. More...
 
O2time o2_set_discovery_period (O2time period)
 Set discovery period. More...
 
O2err o2_hub (int version, const char *public_ip, const char *internal_ip, int tcp_port, int udp_port)
 Connect to a hub. More...
 
O2err o2_get_addresses (const char **public_ip, const char **internal_ip, int *port)
 Get IP address and TCP connection port number. More...
 
const char * o2_get_proc_name (void)
 Get Public:Local:Port service name string. More...
 
int o2_parse_name (const char *name, char *public_ip, char *internal_ip, int *port)
 Parse Public:Local:Port string and extract fields. More...
 
O2err o2_service_new (const char *service_name)
 Add a service to the current process. More...
 
O2err o2_services_list (void)
 list known services and taps More...
 
O2err o2_services_list_free (void)
 free the list of known services and taps More...
 
const char * o2_service_name (int i)
 get a service name from a saved list of services More...
 
int o2_service_type (int i)
 get a type from a saved list of services More...
 
const char * o2_service_process (int i)
 get a process name from a saved list of services More...
 
const char * o2_service_tapper (int i)
 get a tapper name from a saved list of services More...
 
const char * o2_service_properties (int i)
 get the properties string from a saved list of services More...
 
const char * o2_service_getprop (int i, const char *attr)
 get a property value from a saved list of services More...
 
int o2_service_search (int i, const char *attr, const char *value)
 find a service matching attribute/value pair More...
 
O2err o2_service_set_property (const char *service, const char *attr, const char *value)
 set an attribute and value property for a service More...
 
O2err o2_service_property_free (const char *service, const char *attr)
 remove an attribute and value property from a service More...
 
O2err o2_tap (const char *tappee, const char *tapper, O2tap_send_mode send_mode)
 install tap to copy messages from one service to another More...
 
O2err o2_untap (const char *tappee, const char *tapper)
 remove tap from service More...
 
O2err o2_service_free (const char *service_name)
 Remove a local service. More...
 
O2err o2_method_new (const char *path, const char *typespec, O2method_handler h, const void *user_data, bool coerce, bool parse)
 Add a handler for an address. More...
 
O2err o2_method_free (const char *path)
 remove a path – remove a path and associated handler More...
 
void o2_message_drop_warning (const char *warn, o2_msg_data_ptr msg)
 Default dropped message alert. More...
 
void o2_drop_message (const char *warn, bool free_the_msg)
 Tell world that a message was dropped. More...
 
void o2_message_warnings (void(*warning)(const char *warn, o2_msg_data_ptr msg))
 Enable/Disable warnings for dropped messages. More...
 
O2err o2_poll (void)
 Process current O2 messages. More...
 
int o2_run (int rate)
 Run O2. More...
 
int o2_status (const char *service)
 Check the status of the service. More...
 
const char * o2_status_to_string (int status)
 retrieve text version of an O2status More...
 
O2err o2_can_send (const char *service)
 Test if send_cmd will block. More...
 
int o2_roundtrip (double *mean, double *min)
 Get network round-trip information. More...
 
int o2_clock_set (o2_time_callback gettime, void *rock)
 Provide a time reference to O2. More...
 
O2err o2_message_send (O2message_ptr msg)
 Send an O2 message. (See also macros o2_send and o2_send_cmd). More...
 
O2time o2_time_get (void)
 Get the estimated synchronized global O2 time. More...
 
O2time o2_local_time (void)
 Get the real time using the local O2 clock. More...
 
O2err o2_finish (void)
 release the memory and shut down O2. More...
 
O2err o2_schedule_msg (O2sched_ptr scheduler, O2message_ptr msg)
 
void o2_sleep (int n)
 Suspend for n milliseconds. More...
 
unsigned int o2_hex_to_int (const char *hex)
 Convert hex string to integer. More...
 
void o2_hex_to_dot (const char *hex, char *dot)
 Convert from hex format to dot format IP address. More...
 
 void ((*o2_free_ptr)(void *))
 
void * o2_dbg_malloc (size_t size, const char *file, int line)
 allocate memory More...
 
void o2_dbg_free (void *obj, const char *file, int line)
 free memory allocated by #O2_MALLOC
 
void * o2_dbg_calloc (size_t n, size_t s, const char *file, int line)
 allocate and zero memory (see #O2_MALLOC) More...
 
void o2_mem_check (void *ptr)
 

Variables

O2arg_ptr o2_got_start_array
 
O2arg_ptr o2_got_end_array
 
bool o2_stop_flag
 set this flag to stop o2_run More...
 
const char * o2_ensemble_name
 
bool o2_clock_is_synchronized
 A variable indicating that the clock is the reference or is synchronized to the reference.
 
O2sched o2_gtsched
 Scheduler that schedules according to global (reference) clock time. More...
 
O2sched o2_ltsched
 Scheduler that schedules according to local clock time. More...
 
O2sched_ptr o2_active_sched
 Current scheduler. More...
 
void *(* o2_malloc_ptr )(size_t size))
 

Detailed Description

Macro Definition Documentation

◆ o2_send

#define o2_send (   path,
  time,
  ... 
)

Construct and send O2 message with best effort protocol.

Normally, this constructs and sends an O2 message via UDP. If the destination service is reached via some other network protocol (e.g. Bluetooth), the message is delivered in the lowest latency protocol available, with no guaranteed delivery.

Parameters
pathan address pattern
timewhen to dispatch the message, 0 means right now. In any case, the message is sent to the receiving service as soon as possible. If the message arrives early, it will be held at the service and dispatched as soon as possible after the indicated time.
typestringthe type string for the message. Each character indicates one data item. Allowed type characters are those in "ifsbhtdScmTFNIB". Threee O2 type codes: "[]v" are not supported. (if needed, create a message and use the appropriate o2_add_... function.)
...the data of the message. There is one parameter for each character in the typestring.
Returns
O2_SUCCESS if success. See o2_send_finish for details.

◆ o2_send_cmd

#define o2_send_cmd (   path,
  time,
  ... 
)

Construct and send an O2 message reliably.

Normally, this constructs and sends an O2 message via TCP. If the destination service is reached via some other network protocol (e.g. Bluetooth), the message is delivered using the most reliable protocol available. (Thus, this call is considered a "hint" rather than an absolute requirement.)

If the send would block, the message is held until the socket unblocks, allowing O2 to send the message. In this state, where one message is waiting to be sent, o2_can_send will return O2_BLOCKED. In this state, o2_send_cmd will block until the pending message can be sent. Then o2_send_cmd will try again, possibly re-entering the O2_BLOCKED state with the new message.

Parameters
pathan address pattern
timewhen to dispatch the message, 0 means right now. In any case, the message is sent to the receiving service as soon as possible. If the message arrives early, it will be held at the service and dispatched as soon as possible after the indicated time.
typestringthe type string for the message. Each character indicates one data item. Type codes are defined by O2type.
...the data of the message. There is one parameter for each character in the typestring.
Returns
O2_SUCCESS if success. See o2_send_finish for details.

◆ O2MEM_ALIGN

#define O2MEM_ALIGN   8

get the type string from o2_msg_data_ptr

Type strings begin with the comma (",") character, which is skipped

Typedef Documentation

◆ o2_time_callback

typedef O2time(* o2_time_callback) (void *rock)

signature for callback that defines the reference clock

See o2_clock_set for details.

◆ O2blob

typedef struct O2blob O2blob

The structure for binary large object.

A blob can be passed in an O2 message using the 'b' type. Created by calls to o2_blob_new.

◆ O2message

typedef struct O2message O2message

an O2 message container

Note: This struct represents an O2 message that is stored on the heap. O2message is an alias for O2netmsg. At the o2n (network) abstraction, there is no O2msg_data type.

Note that O2messages are on the heap and can be allocated, scheduled, sent, and freed. In contrast, O2msg_data structures are contained within O2messages and are passed to method handlers, but cannot be allocated, scheduled, sent, or freed. They are always the data field of a containing O2message.

◆ O2method_handler

typedef void(* O2method_handler) (const o2_msg_data_ptr msg, const char *types, O2arg_ptr *argv, int argc, const void *user_data)

callback function to receive an O2 message

Parameters
msgThe full message in host byte order.
typesIf you set a type string in your method creation call, then this type string is provided here. If you did not specify a string, types will be the type string from the message (without the initial ','). If parse_args and coerce_flag were set in the method creation call, types will match the types in argv, but not necessarily the type string or types in msg.
argvAn array of O2arg types containing the values, e.g. if the first argument of the incoming message is of type 'f' then the value will be found in argv[0]->f. (If parse_args was not set in the method creation call, argv will be NULL.) For vectors, specified in types by the sequence "vi", "vh", "vf", or "vd", there will be one pointer in argv pointing to a vector description (the v field in O2arg). For arrays, there are no pointers corresponding to '[' or ']' in the types string; but there is one pointer in argv for each array element.
argcThe number of arguments received. (This is valid even if parse_args was not set in the method creation call.) This is the length of argv. Vectors count as one, array elements count as one each, and arrays themselves are not represented. For example, an empty array ("[]") in the type string adds nothing to the argc count or argv vector.
user_dataThis contains the user_data value passed in the call to the method creation call.

◆ O2msg_data

typedef struct O2msg_data O2msg_data

data part of an O2 message

This data type is used to pass o2 message data to message handlers. It appears many other times in the code. You should NEVER allocate or free an O2msg_data struct. Instead, create a message using o2_send_start, o2_add_*(), and o2_message_finish to get an O2message_ptr. Within the O2message, the data field is an O2msg_data structure. We would use O2message everywhere instead of O2msg_data, but bundles can contain multiple O2msg_data structures without the extra baggage contained in an O2message.

Enumeration Type Documentation

◆ O2type

enum O2type

An enumeration of the O2 message types.

Enumerator
O2_INT32 

32 bit signed integer.

O2_FLOAT 

32 bit IEEE-754 float.

O2_STRING 

NULL terminated string (Standard C).

O2_BLOB 

Binary Large OBject (BLOB) type.

O2_ARRAY_START 

Start array or tuple.

O2_ARRAY_END 

End array or tuple.

O2_INT64 

64 bit signed integer.

O2_TIME 

OSC time type.

O2_DOUBLE 

64 bit IEEE-754 double.

O2_SYMBOL 

Used to distinguish strings and symbols.

O2_CHAR 

8bit char variable (Standard C).

O2_MIDI 

4 byte MIDI packet.

O2_TRUE 

Symbol representing the value True.

O2_FALSE 

Symbol representing the value False.

O2_NIL 

Symbol representing the value Nil.

O2_INFINITUM 

Symbol representing the value Infinitum.

O2_BOOL 

Boolean value returned as either 0 or 1.

O2_VECTOR 

Prefix to indicate a vector.

Function Documentation

◆ o2_can_send()

O2err o2_can_send ( const char *  service)

Test if send_cmd will block.

Parameters
servicethe name of the service.
Returns
O2_SUCCESS if sending a command to this service will not block, or O2_BLOCKED if sending a command to this service will block. O2_FAIL is returned if the service is unknown.

If a process is streaming data to another and the TCP buffers become full, the sender will block. Normally, blocking is short term, but if the receiver is not reading, the sender can block indefinitely. This can lead to deadlock if processes block trying to send to each other. If necessary for responsiveness or deadlock-avoidance, the sender should call o2_can_send before calling o2_send_cmd. If O2_BLOCKED is returned, the caller should defer the call to o2_send_cmd but continue calling o2_poll, and at some point in the future, provided the receiver is receiving, o2_can_send should return O2_SUCCESS.

This works if service forwards messages locally to OSC via TCP. However, if service is provided by another process, and that process forwards messages to an OSC server via TCP, then the return value indicates whether the first hop to the remote process will block. Once the message arrives at the remote process, the hop to the OSC server might be blocked, in which case the remote process will block. This could create any number of problems. It is safer to send to the service using o2_send (UDP). In that case, the remote process will drop the message rather than block. An even better option might be to send directly to the OSC server by creating a local service for it (see o2_osc_delegate). In order for two processes to connect to the same OSC server, it is necessary to use different service names: one for each sending process. Otherwise, one process will take priority and all messages will will flow through that process. If it is not possible for all processes to connect directly to the OSC server, and if blocking must be avoided, the most general solution is to create an O2 service to receive messages and resend them to OSC. The receiver can then detect blocking using o2_can_send and implement any policy or filtering desired.

◆ o2_clock_set()

int o2_clock_set ( o2_time_callback  gettime,
void *  rock 
)

Provide a time reference to O2.

Exactly one process per O2 ensemble should provide a reference clock. All other processes synchronize to the reference. To become the reference, call o2_clock_set.

The time reported by the gettime function will be offset to match the current local time so that local time continues to increase smoothly. You cannot force O2 time to match an external absolute time, but once o2_clock_set is called, the difference between the time reference and O2's local time (as reported by o2_local_time) will be fixed.

Parameters
gettimefunction to get the time in units of seconds. The reference may be operating system time, audio system time, MIDI system time, or any other time source. The times returned by this function must be non-decreasing and must increase by one second per second of real time to close approximation. The value may be NULL, in which case a default time reference will be used.

@parm rock an arbitrary value that is passed to the gettime function. This may be need to provide context. Use NULL if no context is required.

Returns
O2_SUCCESS if success, O2_FAIL if not.

◆ o2_dbg_calloc()

void * o2_dbg_calloc ( size_t  n,
size_t  s,
const char *  file,
int  line 
)

allocate and zero memory (see #O2_MALLOC)

Similar to calloc, but this uses the malloc and free functions provided to O2 through a call to o2_memory().

Parameters
[in]nThe number of objects to allocate.
[in]sizeThe size of each object in bytes.
Returns
The address of newly allocated and zeroed memory, or NULL.

◆ o2_dbg_malloc()

void * o2_dbg_malloc ( size_t  size,
const char *  file,
int  line 
)

allocate memory

O2 allows you to provide custom heap implementations to avoid priority inversion or other real-time problems. Normally, you should not need to explicitly allocate memory since O2 functions are provided to allocate, construct, and deallocate messages, but if you need to allocate memory, especially in an O2 message handler callback, i.e. within the sphere of O2 execution, you should use #O2_MALLOC, #O2_FREE, and #O2_CALLOC and their variants.

The O2lite library shares some code with O2 but not memory allocation. To simplify things, you can just define O2_MALLOC, e.g. to be malloc, and define O2_FREE, e.g. to be free, and the logic here will provide implementations of O2_MALLOCT, O2_MALLOCNT, etc.

◆ o2_drop_message()

void o2_drop_message ( const char *  warn,
bool  free_the_msg 
)

Tell world that a message was dropped.

Parameters
warnA human-readable description of the cause.
free_the_msgif true, frees the message

Applications and libraries can call this function to report dropped messages in the same manner as O2. The default behavior is to call o2_message_drop_warning or another handler installed by a previous call to o2_message_warnings. This function assumes the message is at the head of the list o2_ctx->msgs, so you can access it wtih o2_current_message() and aquire ownership with o2_postpone_delivery().

◆ o2_finish()

O2err o2_finish ( void  )

release the memory and shut down O2.

Close all sockets, free all memory, and restore critical variables so that O2 behaves as if it was never initialized.

Note that o2_finish will close websockets immediately without performing the standard websocket close protocol. The recommended clean shutdown with o2lite running over websockets is: If the O2 host is to shut down, send a message from the browser to the O2 host (see test/websockhost.cpp and its stop_handler function for example.) Then call o2ws_finish() in the browser. If the O2 host is shutting down, have it call o2_poll() for a 100 ms or so, giving O2 a chance to respond to the incoming websocket CLOSE command. This will complete the shutdown on the browser side, avoiding an exception there. Then, the O2 host can call o2_finish to close all sockets and free resources.

Returns
O2_SUCCESS if success, O2_FAIL if not.

◆ o2_get_addresses()

O2err o2_get_addresses ( const char **  public_ip,
const char **  internal_ip,
int *  port 
)

Get IP address and TCP connection port number.

Before calling o2_hub, you need to know the IP address and TCP connection port of another process. This call will retrieve the information, but the mechanism to transfer this information to another O2 process (or all of them) must be implemented outside of O2. (If the local network allows UDP broadcast and all hosts are on the local network, then you do not need this function or o2_hub. Instead, let the discovery protocol exchange process addresses automatically.)

Because O2 must query a STUN server to obtain the public IP address, this function will return O2_FAIL for some time after o2_initialize is called. Typically, the public IP address will be available in less than 1 second, but the STUN calls will continue for 10 seconds before O2 gives up and concludes that the Internet is unreachable, in which case the public_ip is set to 00000000 and this call returns O2_SUCCESS.

If there is no network at all, the internal ip is 7f000001 (localhost) and O2 will still operate, connecting to other O2 processes on the same host.

Parameters
public_ipis a pointer that will be set to either NULL (on failure) or a string of the form "80100a06". The string should not be modified, and the string will be freed by O2 if o2_finish is called.
internal_ipis a pointer that will be set to either NULL (on failure) or a string of the form "c0000006". The string should not be modified, and the string will be freed by O2 if o2_finish is called.
portwill be set to a pointer to the O2 TCP connection port (or NULL on failure).
Returns
O2_SUCCESS if success, O2_FAIL if not.

◆ o2_get_proc_name()

const char * o2_get_proc_name ( void  )

Get Public:Local:Port service name string.

An O2 process is identified by its IP addresses and Port number. An O2 process is a special service name that is automatically created when O2 is initialized. For example, "@6a960032:c0a801b6:55630". By convention, you should always use "_o2" instead. This is an alias for the local process. The full:local:port string for a process is used, for example, in status messages ("/_o2/si") to identify a remote process that is providing the service.

Returns
pointer to the Public:Local:Port string. The result will be NULL if O2 is unable to obtain the host's IP address or unable to obtain a TCP server port.

◆ o2_hex_to_dot()

void o2_hex_to_dot ( const char *  hex,
char *  dot 
)

Convert from hex format to dot format IP address.

O2 uses 8 digit hexadecimal notation for IP addresses, mostly internally. To convert to the more conventional "dot" notation, e.g. "127.0.0.1", call o2_hex_to_dot.

Parameters
hexis a string containing an 8 character hexadecimal IP address.
dotis a memory area of size O2N_IP_LEN or greater where the dot notation is written.

◆ o2_hex_to_int()

unsigned int o2_hex_to_int ( const char *  hex)

Convert hex string to integer.

Parameters
hexa string of hex digits (no minus sign allowed)
Returns
a positive integer

◆ o2_hub()

O2err o2_hub ( int  version,
const char *  public_ip,
const char *  internal_ip,
int  tcp_port,
int  udp_port 
)

Connect to a hub.

A "hub" is an O2 process that shares discovery information with other processes. This is an alternate form of discovery that is completely compatible with the broadcast-based discovery protocol, except (1) you do not need broadcast messages to communicate with a hub, (2) you do need the hub's IP address and port number. If the IP and port number can be shared, e.g. through a server or online database with a fixed address, you can work with networks that disallow broadcast, and you can connect across networks (which will not work with O2's normal discovery protocol if broadcast messages are not delivered across networks). To use a hub, you call o2_hub with the hub's IP address and port. All O2 processes are effectively hubs with no clients, and o2_hub simply connects to the hub as a client. The hub will then send discovery messages for all current and future O2 processes that are discovered, either through the normal discovery protocol or by connecting with the o2_hub call.

o2_hub only works if there is an Internet connection, so connecting through MQTT is likely to be a better solution.

After o2_hub is called, discovery broadcasting is stopped, so if o2_hub fails to connect to another process, you will only discover more processes if they initiate the exchange. You can use o2_hub specifically to disable broadcast-based discovery by passing NULL as the public_ip parameter.

You can call o2_hub multiple times, but the call may simply return O2_FAIL if o2_hub is called more than once before a public IP address is obtained. Each call potentially makes a remote process become a hub for this local process. This might result in duplicate messages when new processes join the O2 ensemble, but duplicate messages are ignored.

Parameters
versionis the version number of of the hub, e.g. 0x20103 for 2.1.3
public_ipthe public IP address of the hub or NULL
internal_ipthe local IP address of the hub
tcp_portthe port number of the hub's TCP port
udp_portthe port number of the hub's UDP port
Returns
O2_SUCCESS if success, O2_FAIL if not, O2_NOT_INITIALIZED if O2 is not initialized.

◆ o2_initialize()

O2err o2_initialize ( const char *  ensemble_name)

Start O2.

If O2 has not been initialized, it is created and intialized. O2 will begin to establish connections to other instances with a matching ensemble name.

Parameters
ensemble_namethe name of the ensemble. O2 will attempt to discover other processes with a matching ensemble name, ignoring all processes with non-matching names.
Returns
O2_SUCCESS if success, O2_FAIL if an error occurs, #O2_RUNNING if already running, O2_BAD_NAME if ensemble_name is NULL.

◆ o2_internet_enable()

O2err o2_internet_enable ( bool  enable)

Disable Internet connections.

Set the default to enable or disable Internet connections to other hosts. This setting can only be changed before O2 is started with o2_initialize() or between calls to o2_finish() and o2_initialize. When o2_initialize() is called and network connections are enabled, O2 will try to obtain a local (internal) IP address. If found, O2 will then try to obtain a public IP address. This may result in a long delay if the Internet cannot be reached. See o2_network_enable for more detail. If Internet connections are disabled, the delay can be avoided and the public IP address is immediately set to 0.0.0.0. Even then, O2 will still attempt to open Internet connections for OSC. OSC connections depend only on network connectivity and ignore the o2_internet_enable setting.

When Internet connections are disabled, O2 processes can still interconnect on the local area network. If an ensemble is expected to run only within the local area network, Internet connections (including possible security threats as well as some overhead in finding the public IP address and setting up MQTT connections) can be blocked using this option.

Internet connections can also be disabled by passing 'I' as a character in the string parameter of o2_debug_flags.

Parameters
enableUse true to enable or false to disable Internet connections.
Returns
O2_SUCCESS if setting is accepted, otherwise O2 is already running and O2_ALREADY_RUNNING is returned. If the network has been disabled (see o2_network_enable), O2_NO_NETWORK is returned.

◆ o2_local_time()

O2time o2_local_time ( void  )

Get the real time using the local O2 clock.

Returns
the local time in seconds

◆ o2_memory()

int o2_memory ( void *  (*malloc)(size_t size),
void((*free)(void *))  ,
char *  first_chunk,
int64_t  size,
bool  mallocp 
)

Tell O2 how to allocate/free memory.

In many C library implementations, the standard implementation of free() must lock a data structure. This can lead to priority inversion if O2 runs at an elevated priority. Furthermore, the standard malloc() and free() do not run in constant (real) time. To avoid these problems, O2 uses it's own memory allocation by default, or you can provide an alternate heap implementation for O2 by calling this function before calling o2_initialize.

The application should always use #O2_MALLOC, #O2_MALLOCT, #O2_MALLOCNT, #O2_CALLOC, and #O2_CALLOCT functions, which add a layer of debugging support and call either the default O2 malloc() and free() functions or the ones provided in this call.

The default configuration corresponds to parameters: NULL, NULL, NULL, 0, true. In other words, O2 will allocate chunks of heap space using the C-library #malloc function as needed.

o2_memory can be called one time before the first call to o2_initialize. The configuration is retained and reused, including #chunk and #size, even if O2 is restarted by calling o2_finish #followed by #o2_intialize.

Parameters
malloca function pointer that behaves like standard malloc() or NULL to use O2's default memory allocator.
freea function pointer that behaves like standard free() or NULL to use O2's default memory free function. (The value should be NULL iff #malloc is NULL.)
first_chunkif #malloc and #free are NULL, #first_chunk provides a memory area from which O2 can allocate as needed. This value may be NULL. In non-NULL, #first_chunk is owned by O2 until the last call to o2_finish, after which the caller should free it (or not – it is possible to use a large static block of memory rather than the heap).
sizeis the size in bytes of #first_chunk. Specify 0 if #first_chunk is NULL.
mallocpif #malloc and #free are NULL, and O2 runs out of memory, either because #first_chunk is NULL to begin with or because it was not big enough to meet the allocation needs of O2, then O2 can be directed to use the C-library #malloc to allocate another chunk of memory by passing #true for #mallocp.
Returns
O2_SUCCESS if succeed, O2_FAIL if not.

◆ o2_message_drop_warning()

void o2_message_drop_warning ( const char *  warn,
o2_msg_data_ptr  msg 
)

Default dropped message alert.

Parameters
warnA human-readable description of the cause.
msga pointer to the message data to be dropped

This default parameter for o2_message_warnings prints the warn string followed by the message to be dropped (if O2_NO_DEBUG is undefined) or the message address and type string (if O2_NO_DEBUG is defined) to stdout. Do not call this function. Use o2_drop_message or #o2_drop_msg_data instead. You can pass this function to o2_message_warnings to restore default warning behavior.

◆ o2_message_print()

void o2_message_print ( O2message_ptr  msg)

print an O2 message to stdout

For debugging, this function will print a human-readable representation of a message and its parameters.

Parameters
msga message to be printed

◆ o2_message_send()

O2err o2_message_send ( O2message_ptr  msg)

Send an O2 message. (See also macros o2_send and o2_send_cmd).

Parameters
msgpoints to an O2 message.
Returns
O2_SUCCESS if success, O2_FAIL if not.

After the call, the msg parameter is "owned" by O2, which will free it. Therefore, do not free msg after calling o2_message_send.

◆ o2_message_warnings()

void o2_message_warnings ( void(*)(const char *warn, o2_msg_data_ptr msg)  warning)

Enable/Disable warnings for dropped messages.

Parameters
warninga function such as o2_message_drop_warning to issue the warning or NULL.

It can be very annoying when O2 messages are not received due to an error in setting up a message handler with o2_method_new. Therefore, the default behavior is to print a message whenever an O2 message is dropped. (Exceptions: messages addressed to "!_o2/si" but not handled are dropped without warning. This address is described under "API Messages." Undelivered messages when O2 is shut down with o2_finish are silently deleted.) The default warning behavior is to print a warning to stdout, but since that does not always exist, you can call this function to install a custom handler.

Call #o2_message_warnings(NULL) to disable any warnings.

The #warning function, if any, must not free #msg, which is owned and eventually freed by the caller.

No warning is printed if a message is lost by the network or queued for network delivery when the receiver closes the socket or loses its connection. Also, UDP messages have no acknowledgements, so there is no way to warn if a UDP message from O2 is intended for an OSC server that does not even exist. With O2, however, failed or non-existent remote services are detected (eventually), resulting in warnings even for UDP messages.

◆ o2_method_free()

O2err o2_method_free ( const char *  path)

remove a path – remove a path and associated handler

To remove a handler, call this function to remove it from the handler lookup structure. You can also remove a subtree of handlers, e.g. if path is /W/X, it will remove any handlers on paths starting with /W/X, e.g. /W/X/Y and /W/X/Z.

Parameters
pathThe path of the method
Returns
If success, return O2_SUCCESS. If not, return O2_FAIL.

◆ o2_method_new()

O2err o2_method_new ( const char *  path,
const char *  typespec,
O2method_handler  h,
const void *  user_data,
bool  coerce,
bool  parse 
)

Add a handler for an address.

Parameters
paththe address including the service name.
typespecthe types of parameters, use "" for no parameters and NULL for no type checking
hthe handler
user_datapointer saved and passed to handler
coerceis true if you want to allow automatic coercion of types. coerce is ignored if parse is false.
parseis true if you want O2 to construct an argv argument vector to pass to the handle
Returns
O2_SUCCESS if succeed, O2_FAIL if not.

If the address is only the service name with no trailing slash, the handler will match any message to the service. Pay attention to the parse flag – if true and types do not match, the message will not be delivered.

Addresses should not conflict: An address should not match another address, and for every pair of addresses X and Y, X/ should not be a prefix of Y. Otherwise, the last handler added will remove all conflicting handlers.

A handler for /W/X will not handle children at /W/X/Y and /W/X/Z, and in fact causes messages to /W/X/Y and /W/X/Z to be ignored. The only way to receive multiples addresses with one handler is to handle /W, i.e. to install a handler for just the service.

◆ o2_msg_data_print()

void o2_msg_data_print ( o2_msg_data_ptr  msg)

print a message from msg_data_ptr to stdout

For debugging, this function will print a human-readable representation of a message and its parameters.

Parameters
msga message to be printed

◆ o2_network_enable()

O2err o2_network_enable ( bool  enable)

Disable external nework connections.

Set the default to enable or disable network connections to other hosts. This setting can only be changed before O2 is started with o2_initialize() or between calls to o2_finish() and o2_initialize. When o2_initialize() is called and network connections are enabled, O2 will try to obtain a local (internal) IP address. If found, O2 will then try to obtain a public IP address (assuming Internet connections are enabled, see o2_internet_enable). At the conclusion of this 2-stage initialization, the process will receive an O2 name of the form :internal:port. If a public IP address is not found, the public port is 0.0.0.0, which indicates no Internet connection. If no local IP address is found, or if the only known address is 127.0.0.1 (localhost), then the local IP address becomes 127.0.0.1 with the public IP address 0.0.0.0. If the default for network connections is set to false with o2_network_enable, initialization immediately sets the lcoal IP to 127.0.0.1 and the public IP to 0.0.0.0. In any case, if the local IP is 127.0.0.1, then discovery messages are only sent to localhost, and no discovery messages are broadcast. Even then, O2 can still open connections for OSC. OSC connections depend only on network connectivity and ignore the o2_network_enable setting.

When the network is disabled, O2 processes can still interconnect within the local host. If an ensemble is expected to run only within local host processes, external connections (including possible security threats) can be blocked using this option.

Network access can also be disabled by passing 'N' as a character in the string parameter of o2_debug_flags.

Parameters
enableUse true to enable or false to disable networking.
Returns
O2_SUCCESS if setting is accepted, otherwise O2 is already running and O2_ALREADY_RUNNING is returned.

◆ o2_parse_name()

int o2_parse_name ( const char *  name,
char *  public_ip,
char *  internal_ip,
int *  port 
)

Parse Public:Local:Port string and extract fields.

Parameters
namethe full PublicIP:LocalIP:PortNumber string
public_ipaddress where public_ip is written, at least O2N_IP_LEN long
internal_ipaddress where internal_ip is written, at least O2N_IP_LEN long
portaddress where the port number is written
Returns
O2_SUCCESS if all parameters were written, otherwise O2_FAIL

◆ o2_poll()

O2err o2_poll ( void  )

Process current O2 messages.

Since O2 does not create a thread and O2 requires active processing to establish and maintain connections, the O2 programmer (user) should call o2_poll periodically, even if not offering a service. o2_poll runs a discovery protocol to find and connect to other processes, runs a clock synchronization protocol to establish valid time stamps, and handles incoming messages to all services. o2_poll should be called at least 10 times per second. Messages can only be delivered during a call to o2_poll so more frequent calls will generally lower the message latency as well as the accuracy of the clock synchronization (at the cost of greater CPU utilization). Human perception of timing jitter is on the order of 10ms, so polling rates of 200 to 1000 are advised in situations where rhythmic accuracy is expected.

Returns
0 (O2_SUCCESS) if succeed, -1 (O2_FAIL) if not.

◆ o2_roundtrip()

int o2_roundtrip ( double *  mean,
double *  min 
)

Get network round-trip information.

Returns
If clock is synchronized, return O2_SUCCESS and set *mean to the mean round-trip time and *min to the minimum round-trip time of the last 5 (where 5 is the value of CLOCK_SYNC_HISTORY_LEN) clock sync requests. Otherwise, O2_FAIL is returned and *mean and *min are unaltered.

Note: You can get this information from a remote process by sending a message to !@public:internal:port/cs/rt, where @public:internal:port is the :internal:port string for a process. (One way to get this is to call o2_get_addresses and construct a:internal:port process name from the information returned. But then you can just call o2_roundtrip for the local process round trip information. To get remote process names, you can create a handler for /_o2/si. The process name is provided whenever one of its services is created or otherwise changes status.) The type string for !@public:internal:port/cs/rt is "s", and the parameter is an O2 address. When the message is received, a reply is sent to the address with the type string "sff", and the parameters are (1) the process:internal:port name, (2) the mean of recent round trip times to the reference clock, and (3) the minimum of recent round trip times. (The clock is set using the minimum, so this number is an upper bound on the clock skew for this process.

◆ o2_run()

int o2_run ( int  rate)

Run O2.

Call o2_poll at the rate (in Hz) indicated. Returns if a handler sets o2_stop_flag to non-zero.

◆ o2_schedule_msg()

O2err o2_schedule_msg ( O2sched_ptr  scheduler,
O2message_ptr  msg 
)

/brief Schedule a message.

Rather than sending a message, messages can be directly scheduled. This is particulary useful if you want to schedule activity before clock synchronization is achieved. For example, you might want to poll every second waiting for clock synchronization. In that case, you need to use the local scheduler (o2_ltsched). o2_send will use the global time scheduler (o2_gtsched), so your only option is to construct a message and call o2_schedule_msg.

Parameters
schedulera pointer to a scheduler (&o2_ltsched or &o2_gtsched)
msga pointer to the message to schedule

The message is scheduled for delivery according to its timestamp (which is interpreted as local or global time depending on the scheduler).

The message is delivered immediately if the time is zero or less than the current time; however, to avoid unbounded recursion, messages scheduled within handlers are appended to a "pending messages" queue and delivered after the handler returns.

◆ o2_service_free()

O2err o2_service_free ( const char *  service_name)

Remove a local service.

The #service_name corresponds to the parameter previously passed to o2_service_new or o2_osc_delegate. Note that if an OSC port forwards to this service (see o2_osc_port_new), the port remains open, but the OSC messages will be dropped. See o2_osc_port_free.

Parameters
service_namethe name of the service
Returns
#O2_SUCCSS if success, O2_FAIL if not.

◆ o2_service_getprop()

const char * o2_service_getprop ( int  i,
const char *  attr 
)

get a property value from a saved list of services

Parameters
ithe index of the service with the properties
attran attribute to search for
Returns
the value of that attribute or NULL if not found or invalid or attr is too long (limit is 64 characters). The result is owned by the caller and should be freed using O2_FREE. The returned value has escape characters removed.

◆ o2_service_name()

const char * o2_service_name ( int  i)

get a service name from a saved list of services

See o2_services_list. Do not free the returned value. Instead, call o2_services_list_free. The pointer will be invalid after calling o2_services_list_free.

Parameters
ithe index of the service, starting with zero
Returns
a service name if #i is in range, otherwise NULL. if #i-th service is a tap, then the name returned is the name of the tapped service.

◆ o2_service_new()

O2err o2_service_new ( const char *  service_name)

Add a service to the current process.

Once created, services are "advertised" to other processes with matching ensemble names, and messages are delivered accordingly. E.g. to handle messages addressed to "/synth/volume" you call

o2_service_new("synth");
o2_method_new("/synth/volume", "f", synth_volume_handler, NULL, NULL, true);
O2err o2_service_new(const char *service_name)
Add a service to the current process.
Definition: o2.cpp:1227
O2err o2_method_new(const char *path, const char *typespec, O2method_handler h, const void *user_data, bool coerce, bool parse)
Add a handler for an address.
Definition: o2.cpp:1261

and define synth_volume_handler (see the type declaration for O2method_handler and o2_method_new) User-created service names must begin with a letter. Normally, services should be unique across the ensemble. If #service_name is already locally defined in this process (by a previous call to o2_service_new or o2_osc_delegate), this call will fail, returning O2_SERVICE_EXISTS. If matching service names are defined in two different processes, the process with the highest IP and port number (lexicographically) will provide the service. However, due to the distributed and asynchronous nature of O2, there may be some intervening time (typically a fraction of a second) during which a service is handled by two different processes. Furthermore, the switch to a new service provider could redirect a stream of messages, causing unexpected behavior in the ensemble.

Parameters
service_namethe name of the service
Returns
O2_SUCCESS if success, O2_FAIL if not.

◆ o2_service_process()

const char * o2_service_process ( int  i)

get a process name from a saved list of services

See o2_services_list. Do not free the returned value. Instead, call o2_services_list_free. The pointer will be invalid after calling o2_services_list_free.

Parameters
ithe index of the service
Returns
a process name if #i is in range, otherwise NULL. The process name contains the IP address and TCP port number of the process, making it a unique identifier. If the #i-th service is a tap, the returned value is the tapping process name.

◆ o2_service_properties()

const char * o2_service_properties ( int  i)

get the properties string from a saved list of services

See o2_services_list. Properties have the form: "attr1:value1;attr2:value2;...", where attributes are alphanumeric, and values can be any string with colon represented by "\:", semicolon represented by "\;", and slash represented by "\\". Escape characters are not removed, and the result should not be modified or freed. Properties end in ";".

Do not free the returned value. Instead, call o2_services_list_free. The pointer will be invalid after calling o2_services_list_free.

Parameters
ithe index of the service
Returns
a properties string if #i is in range and the #i-th service is not a tap, otherwise NULL. The string may be empty if the service has no properties.

◆ o2_service_property_free()

O2err o2_service_property_free ( const char *  service,
const char *  attr 
)

remove an attribute and value property from a service

Search for a service offered by the current process named by #service. Then remove both the attribute and value of the property named by #attribute.

Parameters
servicethe name of a service offered by this process
attrthe attribute name
Returns
O2_SUCCESS if successful

◆ o2_service_search()

int o2_service_search ( int  i,
const char *  attr,
const char *  value 
)

find a service matching attribute/value pair

Parameters
ithe index from which to start searching
attrthe attribute to search
valuethe value substring that must match. To match a prefix, use ":prefix"; to match a suffix, use "suffix;"; to make an exact full match, use ":value;". Since the value itself may contain ':', ';', and '\' characters, these must be escaped with '\'. (Unfortunately, in a C or C++ literal string, the '\' itself must also be escaped, so to search for an exact match to the value "x;y", escape ';' to get the 4 character string denoted in C by "x\\;y", then add ':' and ';' to indicate an exact match: ":x\\;y;")
Returns
the index of the first service (at index i or above) where the property named by attr contains value as a substring or prefix or suffix, as indicated by ":" and ";" characters. If no match is found, return -1.

◆ o2_service_set_property()

O2err o2_service_set_property ( const char *  service,
const char *  attr,
const char *  value 
)

set an attribute and value property for a service

Parameters
servicethe name of a service offered by this process
attrthe attribute name
valuethe value string; this string will be escaped. Do not include escape characters in #value
Returns
O2_SUCCESS if successful. O2_FAIL otherwise, for example if the service is not offered by this process.

Note that each call will broadcast the property change to every other O2 process in the ensemble. Therefore properties are not recommended for publishing values frequently to clients, expecially if multiple properties are typically updated in sequence, e.g. X, Y, Z coordinates, which would result in 3 messages to each other process. Consider sending X, Y, Z together in a normal O2 message, and consider using taps if the "publisher" does not know all the "subscribers." Also note that properties can be written by multiple service providers, all offering services with the same name, but properties are only readable from the current service provider. If the current service provider changes to a new process, there can be temporary inconsistent views of service properties across the O2 ensemble.

◆ o2_service_tapper()

const char * o2_service_tapper ( int  i)

get a tapper name from a saved list of services

See o2_services_list. Do not free the returned value. Instead, call o2_services_list_free. The pointer will be invalid after calling o2_services_list_free.

Parameters
ithe index of the service
Returns
if the #i-th service is a tap, return the service name to which tapped messages are sent, otherwise NULL.

◆ o2_service_type()

int o2_service_type ( int  i)

get a type from a saved list of services

See o2_services_list. The return value indicates the type of the service: O2_LOCAL (4) if the service is local, O2_REMOTE (5) if the service is remote, O2_BRIDGE (6) if the service is a bridge to an o2lite, websocket or shared memory process (or some other bridge protocol), O2_TO_OSC (7) if the service delegates to an OSC server, and O2_TAP (8) for each tapper of the service.

Parameters
ithe index of the service, starting with zero
Returns
a service type, or O2_FAIL if #i is not in range.

◆ o2_services_list()

O2err o2_services_list ( void  )

list known services and taps

Currently active services and taps can be queried by calling o2_services_list. An internal snapshot of services and taps is saved. Information can then be accessed by calling o2_service_name, o2_service_type, o2_service_process, o2_service_tapper, and o2_service_properties. When the information is no longer needed, call o2_services_list_free.

Only active services and their tappers are reported. If there are two services with the same name, only the active one is reported. Taps on active services are reported even if the tapper does not exist.

Returns
O2_SUCCESS if success, O2_FAIL if not.

◆ o2_services_list_free()

O2err o2_services_list_free ( void  )

free the list of known services and taps

Call this function when the information captured by o2_services_list is no longer needed.

Returns
O2_SUCCESS if success, O2_FAIL if not.

◆ o2_set_discovery_period()

O2time o2_set_discovery_period ( O2time  period)

Set discovery period.

O2 discovery messages are broadcast periodically in case a new process has joined the ensemble. The default period is 4 seconds. If there are N processes, each host will receive N/4 discovery messages per second. Since there are 16 discovery ports, a discovery message from given process could be received every 64 seconds. (Note, however, that new processes send more frequently, so if messages are not dropped, discovery of new processes will happen much faster than the worst-case.)

You can change the polling period from 4s by calling this function. The new polling period takes effect when the next discovery message is sent at the end of the current polling period.

However, discovery is limited to approximately 10 incoming messages/second based on the number of known processes, so when more remote processes are discovered, the polling period may increase.

Parameters
periodthe requested polling period; a minimum of 0.1s is enforced; 4s is the default (recommended).
Returns
the previous polling period

◆ o2_sleep()

void o2_sleep ( int  n)

Suspend for n milliseconds.

Parameters
nnumber of milliseconds to sleep

◆ o2_status()

int o2_status ( const char *  service)

Check the status of the service.

Parameters
servicethe name of the service
Returns
  • O2_FAIL if no service is found,
  • O2_LOCAL_NOTIME if the service is local but we have no clock sync yet,
  • O2_REMOTE_NOTIME if the service is remote but we have no clock sync yet,
  • O2_BRIDGE_NOTIME if service is attached by a non-IP link, but we have no clock sync yet (if the non-IP connection is not handled by this process, the service status will be O2_REMOTE_NOTIME),
  • O2_TO_OSC_NOTIME if service forwards to an OSC server but we have no clock sync yet (if the OSC connection is not handled by this process, the service status will be O2_REMOTE_NOTIME),
  • O2_LOCAL if service is local and we have clock sync,
  • O2_REMOTE if service is remote and we have clock sync,
  • O2_BRIDGE if service is handled locally by forwarding to an attached non-IP link, and we have clock sync. (If the non-IP connection is not local, the service status will be O2_REMOTE).
  • O2_TO_OSC if service is handled locally by forwarding to an OSC server and this process has clock sync. (If the OSC connection is not handled locally, the service status will be O2_REMOTE).
service status code.

Note that codes are carefully ordered to allow testing for categories:

  • to test if delivery is possible with a zero (immediate) timestamp, use o2_status(service) > O2_FAIL, o2_status(service) >= 0, or o2_status(service) >= O2_LOCAL_NOTIME.
  • to test if delivery is possible with a non-zero timestamp, use o2_status(service) >= O2_LOCAL. Note that status can change over time, e.g. the status of a remote service will be O2_UNKNOWN until the service is discovered. It will then change to O2_REMOTE_NOTIME until both the sender and receiver achieve clock synchronization and share their synchronized status, and finally the status will become O2_REMOTE.

In the cases with no clock sync, it is safe to send an immediate message with timestamp = 0, but non-zero timestamps are meaningless because either the sending process has no way to obtain a valid timestamp or the receiver has no way to schedule delivery according to a timestamp.

Messages to services are dropped if the service has not been discovered. Timestamped messages (timestamp != 0) are dropped if the sender and receiver are not clock-synchronized. (o2_status(service) >= O2_LOCAL).

A special case is with BRIDGE and OSC services. In these cases, the O2 process offering the service can either schedule the messages locally, sending them according to the timestamp (and suffering some network latency), or if the destination process is synchronized, messages can be forwarded immediately for more precise scheduling at their final destination. O2 does not provide any way for clients/users to determine which of these methods is in effect, and in the case of messages being forwarded by an intermediary O2 process, the originator of the message cannot determine whether the service is offered by an O2 server on the local network, by an OSC server, or through a bridge to another network such as Bluetooth. The status at the originator will be simply O2_REMOTE or O2_REMOTE_NOTIME.

When the status of a service changes, a message is sent with address !_o2/si. The type string is "sis" and the parameters are (1) the service name, (2) the new status, and (3) the :internal:port string of the process that offers (or offered) the service.

◆ o2_status_to_string()

const char * o2_status_to_string ( int  status)

retrieve text version of an O2status

Parameters
statusa status code
Returns
human-readable string representation of status

note that the parameter is of type O2status while o2_status returns int. Therefore o2_status_to_string(o2_status("service")) is an invalid conversion from int to O2status. This is because o2_status can also return an O2err (a negative value). You can pass an error value to o2_status_to_string and it will return "O2_FAIL" (rather than a specific error description). Normally, you will retrieve an int from o2_status and call o2_status_to_string((O2status) stat).

◆ o2_tap()

O2err o2_tap ( const char *  tappee,
const char *  tapper,
O2tap_send_mode  send_mode 
)

install tap to copy messages from one service to another

Parameters
tappeethe service to be tapped
tapperthe existing local service to which copies are sent
send_modeSend the tap message using the same method as the original message with TAP_KEEP, by reliable (TCP) method with TAP_RELIABLE, or by best effort (UDP) method with TAP_BEST_EFFORT.
Returns
O2_SUCCESS if success, O2_FAIL if a tap is not installed.

Have messages delivered to #tappee copied to #tapper. After this call, messages to #tappee are first delivered. Then, if #tappee is local and #tapper exists, the message is copied, modified by replacing the service name with #tapper, and sent. There may be multiple taps on a single service, resulting in the delivery of multiple copies.

Taps can be used to implement a publish/subscribe model. The publisher creates a local service and need not install any message handlers. Subscribers install taps on the service to receive messages. It is more efficient for the tappee to be in the publisher process, but actually any process can publish to the service with the added cost of sending a message to the tappee's process.

Messages with timestamps are held according to their timestamps. Tappers receive the messages after the actual message dispatch. Therefore, publish/subscribe using taps cannot achieve precise timing with timestamps. (The recommended approach to timed publish/subscribe is to use properties: A service subscribing to X sets a property such as "X-subscribe:yes". Publishers for X periodically search for services with the ";X-subscribe:" attribute with value "yes" and (re)build a subscriber list. To publish, the publisher explicitly sends a timed message to each subscriber.)

Bundles are first unbundled before delivery, so tappers will not receive bundles. Note that bundled messages each contain a timestamp and service name which might differ from those of the bundle, so tapping is only applied individually to each bundled message when it is actually delivered.

While services are normally independent of processes (for example, a new service can override an existing one in another process), tappers are tied to processes and cannot be overridden by another service of the same name. It is not redirected to another service provider. Also, unlike ordinary message delivery that delivers one message even if there are multiple processes offering the service, there can be multiple tappers, even with the same service names, and each tapper receives a copy of every message delivered to the tappee.

The lifetime of a tap is independent of the lifetimes of the tappee and the tapper, but the tap is tied to a process, so if the process is terminated, the tap is destroyed throughout the O2 ensemble. For example, if the tappee's process crashes and restarts, and if the tapper belongs to another process that survives, then the tap will be reinstated on the tappee. Similarly, a tap may be created before the tappee or the tapper. Calling o2_service_free on a tapper will not free the tap, so tap messages will continue to be delivered to the tap's process, and if the tappper service is (re)created, these tap messages will be delivered to the new tapper service.

◆ o2_time_get()

O2time o2_time_get ( void  )

Get the estimated synchronized global O2 time.

This function returns a valid value either after you call o2_clock_set, making the local clock the reference clock for the O2 ensemble, or after O2 has finished discovering and synchronizing with the reference clock. Until then, -1 is returned.

The clock accuracy depends upon network latency, how often o2_poll is called, and other factors, but

Returns
the time in seconds, or -1 if global (reference) time is unknown.

◆ o2_untap()

O2err o2_untap ( const char *  tappee,
const char *  tapper 
)

remove tap from service

Parameters
tappeethe service that is tapped
tapperthe service to which copies are sent
Returns
O2_SUCCESS if success, O2_FAIL if no tap was removed.

Remove a previously installed tap

◆ o2_version()

int o2_version ( char *  version)

get O2 version number

If version is not NULL, a version string is written in the form major.minor.patch, where fields are up to 3 digits. Thus, the length of version must be at least 12 bytes (including EOS).

Returns
integer representation, e.g. version 2.160.3 is represented by 0x02A003.

Variable Documentation

◆ o2_active_sched

O2sched_ptr o2_active_sched
extern

Current scheduler.

When a timed message is delivered by a scheduler, o2_active_sched is set to pount to the scheduler. A handler that constructs and schedules a message can use this pointer to continue using the same scheduler.

◆ o2_ensemble_name

const char* o2_ensemble_name
extern

TO DO: see each error return has the right error code tests that generate error messages

◆ o2_gtsched

O2sched o2_gtsched
extern

Scheduler that schedules according to global (reference) clock time.

Scheduling on this scheduler (including sending timed messages) will only work after clock synchronization is obtained. Until then, timed message sends will fail and attempts to o2_schedule_msg will fail.

◆ o2_ltsched

O2sched o2_ltsched
extern

Scheduler that schedules according to local clock time.

It may be necessary to schedule events before clock synchronization with the reference clock, or you may want to schedule local processing that ignores any changes in clock time or clock speed needed to stay synchronized with the reference clock (even though these should be small). For example, O2 uses the local time scheduler to schedule the clock synchronization protocol, which of course must run before clock synchronization is obtained.

In these cases, you should schedule messages using o2_ltsched.

◆ o2_stop_flag

bool o2_stop_flag
extern

set this flag to stop o2_run

Some O2 processes will initialize and call o2_run, which is a simple loop that calls o2_poll. To exit the loop, set o2_stop_flag to #true