diff options
Diffstat (limited to 'doc/afb-events-guide.md')
-rw-r--r-- | doc/afb-events-guide.md | 475 |
1 files changed, 0 insertions, 475 deletions
diff --git a/doc/afb-events-guide.md b/doc/afb-events-guide.md deleted file mode 100644 index aaa09be1..00000000 --- a/doc/afb-events-guide.md +++ /dev/null @@ -1,475 +0,0 @@ -Guide for developing with events -================================ - -Signaling agents are services that send events to any clients that -subscribed for receiving it. The sent events carry any data. - -To have a good understanding of how to write a signaling agent, the -actions of subscribing, unsubscribing, producing, sending and receiving -events must be described and explained. - -Overview of events ------------------- - -The basis of a signaling agent is shown in the following figure: - -![scenario of using events](pictures/signaling-basis.svg) - -This figure shows the main role of the signaling framework for the events -propagation. - -For people not familiar with the framework, a signaling agent and -a “binding” are similar. - -### Subscribing and unsubscribing - -Subscribing is the action that makes a client able to receive data from a -signaling agent. Subscription must create resources for generating the data, and -for delivering the data to the client. These two aspects are not handled by the -same piece of software. Generating the data is the responsibility of the -developer of the signaling agent while delivering the data is handled by the -framework. - -When a client subscribes for data, the agent must: - -1. check that the subscription request is correct; -2. establish the computation chain of the required data, if not already - done; -3. create a named event for the computed data, if not already done; -4. ask the framework to establish the subscription to the event for the - request; -5. optionally give indications about the event in the reply to - the client. - -The first two steps are not involving the framework. They are linked to -the business logic of the binding. The request can be any description of -the requested data and the computing stream can be of any nature, this -is specific to the binding. - -As said before, the framework uses and integrates **libsystemd** and its event -loop. Within the framework, **libsystemd** is the standard API/library for -bindings expecting to setup and handle I/O, timer or signal events. - -Steps 3 and 4 are bound to the framework. - -The agent must create an object for handling the propagation of produced -data to its clients. That object is called “event” in the framework. An -event has a name that allows clients to distinguish it from other -events. - -Events are created using the ***afb\_daemon\_make\_event*** function -that takes the name of the event. Example: - -```C - event = afb_daemon_make_event(afb_daemon, name); -``` - -Once created, the event can be used either to push data to its -subscribers or to broadcast data to any listener. - -The event must be used to establish the subscription for the requesting -client. This is done using the ***afb\_req\_subscribe*** function -that takes the current request object and event and associates them -together. Example: - -```C - rc = afb_req_subscribe(afb_req, event); -``` - -When successful, this function make the connection between the event and -the client that emitted the request. The client becomes a subscriber of -the event until it unsubscribes or disconnects. The -***afb\_req\_subscribe*** function will fail if the client -connection is weak: if the request comes from a HTTP link. To receive -signals, the client must be connected. The AGL framework allows connections -using WebSocket. - -The name of the event is either a well known name or an ad hoc name -forged for the use case. - -Let's see a basic example: client A expects to receive the speed in km/h -every second while client B expects the speed in mph twice a second. In -that case, there are two different events because it is not the same -unit and it is not the same frequency. Having two different events -allows to associate clients to the correct event. But this doesn't tell -any word about the name of these events. The designer of the signaling -agent has two options for naming: - -1. names can be the same (“speed” for example) with sent data - self describing itself or having a specific tag (requiring from - clients awareness about requesting both kinds of speed isn't safe). -2. names of the event include the variations (by example: - “speed-km/h-1Hz” and “speed-mph-2Hz”) and, in that case, sent data - can self describe itself or not. - -In both cases, the signaling agent might have to send the name of the -event and/or an associated tag to its client in the reply of the -subscription. This is part of the step 5 above. - -The framework only uses the event (not its name) for subscription, -unsubscription and pushing. - -When the requested data is already generated and the event used for -pushing it already exists, the signaling agent must not instantiate a -new processing chain and must not create a new event object for pushing -data. The signaling agent must reuse the existing chain and event. - -Unsubscribing is made by the signaling agent on a request of its client. -The ***afb\_req\_unsubscribe*** function tells the framework to -remove the requesting client from the event's list of subscribers. -Example: - -```C - afb_req_unsubscribe(afb_req, event); -``` - -Subscription count does not matter to the framework: subscribing the -same client several times has the same effect that subscribing only one -time. Thus, when unsubscribing is invoked, it becomes immediately -effective. - -#### More on naming events - -Within the AGL framework, a signaling agent is a binding that has an API -prefix. This prefix is meant to be unique and to identify the binding -API. The names of the events that this signaling agent creates are -automatically prefixed by the framework, using the API prefix of the -binding. - -Thus, if a signaling agent of API prefix ***api*** creates an event -of name ***event*** and pushes data to that event, the subscribers -will receive an event of name ***api/event***. - -### Generating and pushing signals and data - -This of the responsibility of the designer of the signaling agent to -establish the processing chain for generating events. In many cases, -this can be achieved using I/O or timer or signal events inserted in the -main loop. For this case, the AGL framework uses **libsystemd** and -provide a way to integrates to the main loop of this library using -afb\_daemon\_get\_event\_loop. Example: - -```C - sdev = afb_daemon_get_event_loop(af_daemon); - rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL); -``` - -In some other cases, the events are coming from D-Bus. In that case, the -framework also uses **libsystemd** internally to access D-Bus. It provides -two methods to get the available D-Bus objects, already existing and -bound to the main**libsystemd**event loop. Use either -***afb\_daemon\_get\_system\_bus*** or -***afb\_daemon\_get\_user\_bus*** to get the required instance. Then -use functions of **libsystemd** to handle D-Bus. - -In some rare cases, the generation of the data requires to start a new -thread. - -When a data is generated and ready to be pushed, the signaling agent -should call the function ***afb\_event\_push***. Example: - -```C - rc = afb_event_push(event, JSON); - if (rc == 0) { - stop_generating(event); - afb_event_drop(event); - } -``` - -The function ***afb\_event\_push*** pushes json data to all the -subscribers. It then returns the count of subscribers. When the count is -zero, there is no subscriber listening for the event. The example above -shows that in that case, the signaling agent stops to generate data for -the event and delete the event using afb\_event\_drop. This is one -possible option. Other valuable options are: do nothing and continue to -generate and push the event or just stop to generate and push the data -but keep the event existing. - -### Receiving the signals - -Understanding what a client expects when it receives signals, events or -data shall be the most important topic of the designer of a signaling -agent. The good point here is that because JSON[^1] is the exchange -format, structured data can be sent in a flexible way. - -The good design is to allow as much as possible the client to describe -what is needed with the goal to optimize the processing to the -requirements only. - -### The exceptional case of wide broadcast - -Some data or events have so much importance that they can be widely -broadcasted to alert any listening client. Examples of such an alert -are: - -- system is entering/leaving “power safe” mode -- system is shutting down -- the car starts/stops moving -- ... - -An event can be broadcasted using one of the two following methods: -***afb\_daemon\_broadcast\_event*** or -***afb\_event\_broadcast***. - -Example 1: - -```C - afb_daemon_broadcast_event(afb_daemon, name, json); -``` - -Example 2: - -```C - event = afb_daemon_make_event(afb_daemon, name); - . . . . - afb_event_broadcast(event, json); -``` - -As for other events, the name of events broadcasted using -***afb\_daemon\_broadcast\_event*** are automatically prefixed by -the framework with API prefix of the binding (signaling agent). - -Reference of functions ----------------------- - -### Function afb\_event afb\_daemon\_make\_event - -The function ***afb\_daemon\_make\_event*** that is defined as below: - -```C -/* - * Creates an event of 'name' and returns it. - * 'daemon' MUST be the daemon given in interface when activating the binding. - */ -struct afb_event afb_daemon_make_event(struct afb_daemon daemon, const char *name); -``` - -The daemon is the handler to the application framework binder daemon -received during initialisation steps of the binding. - -Calling the function ***afb\_daemon\_make\_event*** within the initialisation -function ***afbBindingV1Register*** will _fail_ because the binding -name is not known at this time. - -The correct way to create the event at initialisation is to call the function -***afb\_daemon\_make\_event*** within the initialisation -function ***afbBindingV1ServiceInit***. - -### Function afb\_event\_push - -The function ***afb\_event\_push*** is defined as below: - -```C -/* - * Pushes the 'event' with the data 'object' to its observers. - * 'object' can be NULL. - * - * For convenience, the function calls 'json_object_put' for object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - * - * Returns the count of clients that received the event. - */ -int afb_event_push(struct afb_event event, struct json_object *object); -``` - -As the function ***afb\_event\_push*** returns 0 when there is no -more subscriber, a binding can remove such unexpected event using the -function ***afb\_event\_drop***. - -### Function afb\_event\_drop - -The function ***afb\_event\_drop*** is defined as below: - -```C -/* - * Drops the data associated to the event - * After calling this function, the event - * MUST NOT BE USED ANYMORE. - */ -void afb_event_drop(struct afb_event event); -``` - -### Function afb\_req\_subscribe - -The function ***afb\_req\_subscribe*** is defined as below: - -```C -/* - * Establishes for the client link identified by 'req' a subscription - * to the 'event'. - * Returns 0 in case of successful subscription or -1 in case of error. - */ -int afb_req_subscribe(struct afb_req req, struct afb_event event); -``` - -The subscription adds the client of the request to the list of subscribers -to the event. - -### Function afb\_req\_unsubscribe - -The function ***afb\_req\_unsubscribe*** is defined as -below: - -```C -/* - * Revokes the subscription established to the 'event' for the client - * link identified by 'req'. - * Returns 0 in case of successful unsubscription or -1 in case of error. - */ -int afb_req_unsubscribe(struct afb_req req, struct afb_event event); -``` - -The unsubscription removes the client of the request of the list of subscribers -to the event. -When the list of subscribers to the event becomes empty, -the function ***afb\_event\_push*** will return zero. - -### Function afb\_event\_broadcast - -The function ***afb\_event\_broadcast*** is defined as below: - -```C -/* - * Broadcasts widely the 'event' with the data 'object'. - * 'object' can be NULL. - * - * For convenience, the function calls 'json_object_put' for 'object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - * - * Returns the count of clients that received the event. - */ -int afb_event_broadcast(struct afb_event event, struct json_object *object); -``` - -This uses an existing event (created with ***afb\_daemon\_make\_event***) -for broadcasting an event having its name. - - -### Function afb\_daemon\_broadcast\_event - -The function ***afb\_daemon\_broadcast\_event*** is defined as below: - -```C -/* - * Broadcasts widely the event of 'name' with the data 'object'. - * 'object' can be NULL. - * 'daemon' MUST be the daemon given in interface when activating the binding. - * - * For convenience, the function calls 'json_object_put' for 'object'. - * Thus, in the case where 'object' should remain available after - * the function returns, the function 'json_object_get' shall be used. - * - * Returns the count of clients that received the event. - */ -int afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); -``` - -The name is given here explicitly. The name is automatically prefixed -with the name of the binding. For example, a binding of prefix "xxx" -would broadcat the event "xxx/name". - -### Function afbBindingV1ServiceEvent - -Binding can implement function **afbBindingV1ServiceEvent** which will -be called when an event is broadcasted or if service subscribed to an event. -That allow a service to react to an event and do what it is to do if this is -relevant for it (ie: car back camera detects imminent collision and broadcast -it, then appropriate service enable parking brake.). Here is the -**afbBindingV1ServiceEvent** definition: - -```C -/* - * When a binding have an implementation of the function 'afbBindingV1ServiceEvent', - * defined below, the framework calls that function for any broadcasted event or for - * events that the service subscribed to in its name. - * - * It receive the 'event' name and its related data in 'object' (be aware that 'object' - * might be NULL). - */ -extern void afbBindingV1ServiceEvent(const char *event, struct json_object *object); - -The binding *tic-tac-toe* broadcasts events when the board changes. -This is done in the function **changed**: -``` - -Architectural digressions -------------------------- - -Based on their dependencies to hardware, signaling agents can be split -into 2 categories: low-level signaling agents and high-level signaling -agents. - -Low-level signaling agents are bound to the hardware and focused on -interfacing and driving. - -High-level signaling agent are independent of the hardware and focused on -providing service. - -This separation (that may in the corner look artificial) aim to help in -the systems design. The main idea here is that high-level signaling -agents are providing “business logic”, also known as “application -logic”, that is proper to the car industry and that can be reused and -that can evolve as a foundation for the future of the industry. - -The implementation of this decomposition may follow 2 paths: strict -separation or soft composition. - -### Strict separation - -The strict separation implements the modularity composition of signaling -agent through the framework. The high-level signaling agent subscribes -to the low level signaling agent using the standard client API. - -Advantages: - -- Modularity -- Separation of responsibilities -- Possible aggregation of multiple sources -- Soft binding of agent good for maintenance - -Drawbacks: - -- Cost of propagation of data (might serialize) -- Difficulties to abstract low-level signaling agent or to find a - trade-off between abstracting and specializing - -The key is modularity versus cost of propagation. It can be partly -solved when logical group of signaling agent are launched together in -the same binder process. In that particular case, the cost of -propagation of data between agents is reduced[^2] because there is no -serialization. - -This reduction of the propagation cost (and of the resources used) -precludes implementation of strong security between the agents because -they share the same memory. - -### Soft composition - -The soft composition implements the business logic of high-level -signaling agents as libraries that can then be used directly by the low -level signaling agents. - -Advantages: - -- No propagation: same memory, sharing of native structures - -Drawbacks: - -- Cannot be used for aggregation of several sources -- Difficulties to abstract low-level signaling agent or to find a - trade-off between abstracting and specializing -- Source code binding not good for maintenance - -[^1]: There are two aspect in using JSON: the first is the flexible data - structure that mixes common types (booleans, numbers, strings, - arrays, dictionaries, nulls), the second, is the streaming - specification. Streaming is often seen as the bottleneck of using - JSON (see http://bjson.org). When the agent share the same process, - there is no streaming at all. - -[^2]: Within the same process, there is not serialization, the - propagation has the cost of wrapping a json data and calling - callbacks with the benefit of having a powerful callback manager: - the event mechanism of the framework. |