Controller API overview#
This page describes the main APIs available in the controller library.
aes70::controller::connection
#
Represents a connection to a remote device. How connections are established
depends on the platform. When using libuv, libuv::tcp::connect
can be used
to create a controller connection to a device.
Class Declaration#
namespace aes70::controller
{
class connection
{
public:
// Close this connection.
void close();
// Register for connection close events, e.g. in order to
// re-establish the connection.
subscription onclose(std::function<void(const connection &)> callback);
};
}
aes70::controller::object
#
Base class for all controller objects. Objects behave like value types, they store no state except for the connection object and the object number. All concrete AES70 controller object types extend the following class.
Class Declaration#
namespace aes70::controller
{
class object
{
public:
object(std::shared_ptr<connection> connection, uint32_t object_number);
object(const object &other);
std::shared_ptr<connection> get_connection() const;
uint32_t get_object_number() const;
};
}
aes70::controller::device
#
Overview#
The aes70::controller::device
class can be created from a connection and contains getters for
the global objects in an OCA device.
Class Declaration#
namespace aes70::controller
{
class device
{
public:
device(std::sharded_ptr<connection> con);
OcaAudioProcessingManager AudioProcessingManager();
OcaCodingManager CodingManager();
OcaDeviceManager DeviceManager();
OcaDeviceTimeManager DeviceTimeManager();
OcaDiagnosticManager DiagnosticManager();
OcaFirmwareManager FirmwareManager();
OcaLibraryManager LibraryManager();
OcaMediaClockManager MediaClockManager();
OcaNetworkManager NetworkManager();
OcaPowerManager PowerManager();
OcaSecurityManager SecurityManager();
OcaSubscriptionManager SubscriptionManager();
OcaBlock Root();
}
}
aes70::controller::failure
#
This value type represents failure situations when interacting with device using the controller library.
Generally the possible failure cases are:
- Errors return from remote method calls, i.e. the result of an OCA method a status other than
OcaStatus::OK
. - Failures that result from a local condition, e.g.
- Connection loss
- Failure to decode a response
- Notification exceptions received from a device when using AES70-2024 EV2
Failures can be permanent
or temporary
. A temporary failure could be received in situations where
the failure could be automatically recovered.
Class Declaration#
namespace aes70::controller
{
class failure
{
public:
/**
* Constructs a failure for a non-zero remote status.
*/
failure(OcaStatus status, bool permanent = true);
/**
* Constructs a failure for a local failure reason.
*/
failure(local_failure reason, bool permanent = true);
/**
* Constructs a failure for a notification exception.
*/
failure(notification_exception reason, bool permanent = true);
/**
* Returns true if this is a local failure, e.g. connection loss.
*/
bool is_local_failure() const;
/**
* Returns true if this is a remote failure due to method call
* which failed remotely, e.g. the command failed due
* to OcaStatus::NotImplemented.
*/
bool is_remote_failure() const;
/**
* Returns true if this is a notification exception.
*/
bool is_notification_exception() const;
/**
* Returns true if this returns a connection close.
* Can be called only if is_local_failure() returns true.
*/
bool is_connection_closed() const;
/**
* Returns the local failure reason.
* Can be called only if is_local_failure() returns true.
*/
local_failure get_local_failure() const;
/**
* Returns the remote failure reason.
* Can be called only if is_remote_failure() returns true.
*/
OcaStatus get_remote_failure() const;
/**
* Returns the notification exception.
* Can be called only if is_notification_exception() returns true.
*/
const struct notification_exception &get_notification_exception() const;
bool is_permanent() const;
std::string what() const;
private:
std::variant<local_failure, OcaStatus, notification_exception> data;
bool permanent_ = true;
};
}
aes70::controller::subscription
#
In the libaes70 controller library, event subscriptions are managed through
the aes70::controller::subscription
type.
Overview#
A subscription represents a live registration with an event, which can be either a subscription to a remote event, as well as local events such as the close event on a connection. It ensures that the subscription is automatically cleaned up when no longer needed, following RAII (Resource Acquisition Is Initialization) principles.
Internally, subscription holds a std::shared_ptr<void>
.
As long as this shared pointer is alive, the subscription remains active.
Once the last subscription handle goes out of scope or is reset, the shared object is destroyed, and the library removes the subscription from the device.
This makes subscriptions exception-safe and lifecycle-driven: forgetting to unsubscribe manually will not leak registrations.
Class Declaration#
namespace aes70::controller
{
class subscription
{
public:
// creates an empty subscription.
subscription();
subscription(std::nullptr_t);
template <typename T>
subscription(const std::shared_ptr<T> &ptr);
template <typename T>
subscription(std::shared_ptr<T> &&ptr) noexcept;
// unsubscribes a subscription
void unsubscribe();
// returns true if this subscription is still alive.
bool is_active() const;
private:
std::shared_ptr<void> handle_;
}
}
aes70::controller::path_observer
#
The path_observer
class can be used to locate objects in a device based on their path name and type.
Class Declaraction#
namespace aes70::controller
{
class path_observer
{
/**
* Creates a path observer for the given block.
*/
path_observer(const OcaBlock &remote);
/**
* Creates a path observer for the root block in the given device.
*/
path_observer(device &dev);
/**
* Observes an object with the given path name. The API expects any number
* of strings, each representing the path name within a block. The last
* two arguments are a callback and an error callback. The callback must
* specifiy one argument with the type of the expected aes70 class. The
* error handler will be called with an instance of
* aes70::controller::failure.
*
* Note that the observation callback can be called multiple times for
* different objects, in case that they have the same path names and
* matching types.
*
* Also note that the callback may return a subscription, which is kept
* alive as long as the objects exists.
*/
template <typename... TN>
subscription observe(
std::string Role, std::string ChildRole, TN &&...args);
};
}
The requested object type is detected from the first argument of the passed result
callback. For example, when looking for an object of type OcaGain
with the path
name MyActuators/MyGain
:
auto observer = std::make_shared<aes70::controller::path_observer>(device);
observer->observe(
"MyActuators", "MyGain",
[&](aes70::controller::OcaGain gain) {
std::cerr << "Found OcaGain." << std::endl;
return gain.observeGain(
[&](float value) {
std::cerr << "Gain: " << value << std::endl;
},
on_failure);
},
on_failure));
Here the callback passed to observe()
returns the subscription
returned
by observeGain()
. This subscription
is kept alive by the path_observer
as long as the object exists in the device.
When the object disappears (e.g. when it is removed from the block MyActuators
),
the returned subscription will be unsubscribed. When the object re-appears the
callback will be called again.
Note
path_observer is not state-less. It is important to keep the object around as long as the subscriptions are required.
aes70::controller::member_observer
#
member_observer
can be used to observe members in a block based on their
type.
Class Declaraction#
namespace aes70::controller
{
class member_observer
{
/**
* Constructs a member observer for the given block.
*/
member_observer(const OcaBlock &_remote);
/**
* Constructs a member observer for the root block of the given device.
*/
member_observer(device &dev);
/**
* Returns true if this observer has failed. This happens when e.g. the
* connection to the device has been lost.
*/
bool has_failed() const;
/**
* Observes members of the block. The callback will be called for each
* members which fits the type of the first argument of callback.
*
* The on_error callback will be called when a failure occurs. This can
* happen e.g. when the connection is lost to the device.
*/
template <typename SuccessFunction>
subscription observe(
SuccessFunction &&callback, failure_callback on_error);
/**
* Same as observe, except that the failure callback will be a generic
* function that prints an error on failure. Not handling failures it
* generally not recommended, but can be useful for simple scripts or
* tests.
*/
template <typename Function> subscription observe(Function &&callback);
/**
* Run internal cleanup, i.e. clean subscribers which have been
* unsubscribed.
*/
void cleanup();
/**
* Returns the number of active subscribers. This is only accurate
* after calling cleaup() or right after an event has been raised.
*/
size_t subscriber_count();
/**
* Unsubscribe all observers.
*/
void unsubscribe();
};
}
aes70::controller::iterate_block
#
iterate_block
can be used to iterate members in a block based on their type.
Declaraction#
namespace aes70::controller
{
template <typename... TN>
static void iterate_block(
OcaBlock block, TN &&...handlers);
}
aes70::controller::iterate_block_recursively
#
iterate_block_recursively
can be used to iterate members in a block based on their type.
Declaraction#
namespace aes70::controller
{
template <typename... TN>
static void iterate_block_recursively(
OcaBlock block, TN &&...handlers);
}