/**

@page start_page Getting Started

This page includes a guide on how to write a simple YANG module and then get Sysrepo to handle its data for us either as a @ref sysrepo-plugind plugin or a stand-alone daemon. It is best to have at least a basic understanding of Sysrepo before continuing.

@section yang_module Writing a simple YANG module

For any device you want to manage with Sysrepo you will need a **YANG module** of the device. The language is quite rich and allows for description of almost any system. As an example, an **oven** will be fully managed by Sysrepo. All basic parts of YANG will be covered, namely configuration data, state data, RPCs, and notifications.

To simplify things, our oven is a cheap model that has only a switch for turning it on and off and a slider to set the temperature. However, it can provide information about the actual temperature inside and also notify the cook when the internal temperature reaches the configured temperature. Furthermore, the raw food can be prepared in advance and the oven can automatically put it inside or remove it if prompted. That leaves us with this YANG model:

~~~~~~~~~~~~~~{.yang}
module oven {
    namespace "urn:sysrepo:oven";
    prefix ov;

    revision 2018-01-19 {
        description "Initial revision.";
    }

    typedef oven-temperature {
        description "Temperature range that is accepted by the oven.";
        type uint8 {
            range "0..250";
        }
    }

    container oven {
        description "Configuration container of the oven.";

        leaf turned-on {
            description "Main switch determining whether the oven is on or off.";
            type boolean;
            default false;
        }

        leaf temperature {
            description "Slider for configuring the desired temperature.";
            type oven-temperature;
            default 0;
        }
    }

    container oven-state {
        description "State data container of the oven.";
        config false;

        leaf temperature {
            description "Actual temperature inside the oven.";
            type oven-temperature;
        }

        leaf food-inside {
            description "Informs whether the food is inside the oven or not.";
            type boolean;
        }
    }

    rpc insert-food {
        description "Operation to order the oven to put the prepared food inside.";

        input {
            leaf time {
                description "Parameter determining when to perform the operation.";
                type enumeration {
                    enum now {
                        description "Put the food in the oven immediately.";
                    }
                    enum on-oven-ready {
                        description
                            "Put the food in once the temperature inside
                             the oven is at least the configured one. If it
                             is already, the behaviour is similar to 'now'.";
                    }
                }
            }
        }
    }

    rpc remove-food {
        description "Operation to order the oven to take the food out.";
    }

    notification oven-ready {
        description
            "Event of the configured temperature matching the actual
             temperature inside the oven. If the configured temperature
             is lower than the actual one, no notification is generated
             when the oven cools down to the configured temperature.";
    }
}
~~~~~~~~~~~~~~

For more complex models and their handling look inside the `examples` directory.

@section plugin Oven Plugin

Here it will be step-by-step explained how to write proper @ref plugins that will manage the oven. All code snippets
are taken from the actual implementation fo this plugin.

@subsection init_cleanup Initialization & Cleanup

There are only 2 functions that Sysrepo plugin must define and that will be called on the plugin
manager `sysrepo-plugind` start and termination. They are `int sr_plugin_init_cb(sr_session_ctx_t *session, void **private_ctx)`
and `void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *private_ctx)`.

@subsubsection init Initialization

In the initialization function you must generally initialize the device and create subscriptions
to any relevant YANG nodes.
~~~{.c}
    food_inside = 0;
    insert_food_on_ready = 0;
    oven_temperature = 25;
~~~
To start with, the oven must certainly be informed about any changes in its
configuration parameters so it is easiest to subscribe to the whole module. The flag #SR_SUBSCR_EV_ENABLED is set
so that independently of the state of the oven (device) at the moment `sysrepo-plugind` starts, the Sysrepo startup
configuration is applied to the device and consistency is kept. The other flag, #SR_SUBSCR_APPLY_ONLY is used
so that the callback is not called to verify any pending changes. For our example, as long as the value is
valid based on YANG restrictions, it is always correct. More details at the page of sr_module_change_subscribe().

It is also possible to subscribe to an arbitrary configuration data subtree instead but it is not needed for this example.
~~~{.c}
    rc = sr_module_change_subscribe(session, "oven", oven_config_change_cb, NULL, 0,
            SR_SUBSCR_EV_ENABLED | SR_SUBSCR_APPLY_ONLY, &subscription);
~~~
Then, as there are also state data in the oven model, a subscription that marks this plugin to be a (exclusive)
provider of them is performed. It is called whenever Sysrepo needs the state data subtree, usually when a client
asks for them.

Worth noting is that the same `subscription` object was used and so the flag #SR_SUBSCR_CTX_REUSE had to be specified.
~~~{.c}
    rc = sr_dp_get_items_subscribe(session, "/oven:oven-state", oven_state_cb, NULL, SR_SUBSCR_CTX_REUSE, &subscription);
~~~
Lastly, the plugin will also handle any RPC calls, which also need subsciptions.
~~~{.c}
    rc = sr_rpc_subscribe(session, "/oven:insert-food", oven_insert_food_cb, NULL, SR_SUBSCR_CTX_REUSE, &subscription);
    rc = sr_rpc_subscribe(session, "/oven:remove-food", oven_remove_food_cb, NULL, SR_SUBSCR_CTX_REUSE, &subscription);
~~~
Sysrepo provides macros for plugins to be able to print messages in a unified manner so it is advised to use them.
~~~{.c}
    SRP_LOG_DBG_MSG("OVEN: Oven plugin initialized successfully.");
~~~
The general format is `SRP_LOG_(level)(_MSG)`. Instead of `(level)` the message severity is written, one of `DBG`, `VRB`,
`WRN`, or `ERR`. In the example the suffix `_MSG` was used because there were no additional variable arguments specified.
If there are, this suffix is omitted. The arguments are the same as one would use for the `printf()` function.

@subsubsection cleanup Cleanup

As for cleanup, the tasks performed vary greatly and depend on the device. However, it is always required to properly
terminate the subscriptions made in the init function and that is the only work required in this example.
~~~{.c}
    sr_unsubscribe(session, subscription);
~~~
`subscription` was defined as a global variable to simplify the code but it is possible to use `private_ctx`, for instance,
to store it possibly also with any additional data your application needs. All the other callbacks assigned before can use
the same mechanism for passing additional data if needed.

@subsection configuration Configuration Data

In the example it is subscribed for the module changes with `oven_config_change_cb`. The code seen here is
a simplification of the actual code but is better for understanding what the callback should do.
~~~{.c}
static int
oven_config_change_cb(sr_session_ctx_t *session, const char *module_name, sr_notif_event_t event, void *private_ctx)
{
    int rc;
    sr_val_t *val;

    rc = sr_get_item(session, "/oven:oven/temperature", &val);
    if (rc != SR_ERR_OK) {
        goto sr_error;
    }

    (apply the temperature to the device)
    sr_free_val(val);

    rc = sr_get_item(session, "/oven:oven/turned-on", &val);
    if (rc != SR_ERR_OK) {
        goto sr_error;
    }

    (apply the switch state to the device)
    sr_free_val(val);

    return SR_ERR_OK;

sr_error:
    SRP_LOG_ERR("OVEN: Oven config change callback failed: %s.", sr_strerror(rc));
    return rc;
}
~~~
Firstly, as can be observed, the `event` variable is ignored. In our example we perform the same actions whether
the event equals to `SR_EV_ENABLED` or `SR_EV_APPLY`, it will not equal to any other value bacuse of the subscription flags.
The meaning of other values is explained in the documentation (#sr_notif_event_e).

Then, all the relevant data nodes are read and applied. This approach is the most simple one and cannot be always
employed but is fine in this case because it is possible to re-apply changes without any effect. More elaborate machanism,
which returns the changes, is using sr_get_changes_iter() with the provided session and thus getting only
the specific changed values.

@subsection state State Data
~~~{.c}
static int
oven_state_cb(const char *xpath, sr_val_t **values, size_t *values_cnt, void *private_ctx)
{
    sr_val_t *vals;
    int rc;

    rc = sr_new_values(2, &vals);
    if (SR_ERR_OK != rc) {
        return rc;
    }

    sr_val_set_xpath(&vals[0], "/oven:oven-state/temperature");
    vals[0].type = SR_UINT8_T;
    vals[0].data.uint8_val = oven_temperature;

    sr_val_set_xpath(&vals[1], "/oven:oven-state/food-inside");
    vals[1].type = SR_BOOL_T;
    vals[1].data.bool_val = food_inside;

    *values = vals;
    *values_cnt = 2;

    return SR_ERR_OK;
}
~~~
The state data callback is self-explaining. It should create and return direct children of the `xpath` parent
(more in @ref data_providers). As the subscription was only for one container with 2 leaves as children, the `xpath`
can only have one value and will hence be always the same.

@subsection rpc RPC Subscriptions
~~~{.c}
static int
oven_insert_food_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
        sr_val_t **output, size_t *output_cnt, void *private_ctx)
{
    if (food_inside) {
        SRP_LOG_ERR_MSG("OVEN: Food already in the oven.");
        return SR_ERR_OPERATION_FAILED;
    }

    if (strcmp(input[0].data.enum_val, "on-oven-ready") == 0) {
        if (insert_food_on_ready) {
            SRP_LOG_ERR_MSG("OVEN: Food already waiting for the oven to be ready.");
            return SR_ERR_OPERATION_FAILED;
        }
        insert_food_on_ready = 1;
        return SR_ERR_OK;
    }

    insert_food_on_ready = 0;
    food_inside = 1;
    SRP_LOG_DBG_MSG("OVEN: Food put into the oven.");
    return SR_ERR_OK;
}

static int
oven_remove_food_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
        sr_val_t **output, size_t *output_cnt, void *private_ctx)
{
    if (!food_inside) {
        SRP_LOG_ERR_MSG("OVEN: Food not in the oven.");
        return SR_ERR_OPERATION_FAILED;
    }

    food_inside = 0;
    SRP_LOG_DBG_MSG("OVEN: Food taken out of the oven.");
    return SR_ERR_OK;
}
~~~
RPC callbacks should execute the corresponding RPC. `remove-food` does only that. But `insert-food` has some
input parameters defined so they need to be handled. In the same way, if there were some output parameters, they
would need to be created and returned but that is not our case.

@subsection notif Notifications

The provider of a notification does not need to subscribe to anything but simply generate any notifications
whenever they occur.
~~~{.c}
    rc = sr_event_notif_send(sess, "/oven:oven-ready", NULL, 0, SR_EV_NOTIF_DEFAULT);
~~~
It is quite straightforward as can be seen from the example. Additionally, if there were any children nodes
of the notification, they would need to be created and then passed to the function sr_event_notif_send().

@subsection try Trying It Out

The model and full plugin source can be found in `sysrepo/examples/plugins`. Once Sysrepo is built, the `oven` shared library
is stored in `examples/plugins` but it is not installed automatically. Before installing and actually running the plugin it is
best to go carefully through the source code. It is just a small well-documented file so it should not take long and
one should understand the implemented `oven` functionality. Also, most of the information covered in the sections
above is just basic and detailed descriptions of all this mechanisms can be found in other subpages in the parent
@ref dev_guidelines page.

Before being concerned with this particular plugin, there must be `sysrepo` and `netopeer2` (both server and CLI) properly
built and installed. Refer to the corresponding project installation instructions for more help.

Having done that, you must install first the model, then the plugin. How to install a new model is written in
@ref sysrepoctl_install and then you must put the shared library `liboven.so` into @ref plugin-dir.

After that you should be ready to start first `sysrepod`, then `sysrepo-plugind`, `netopeer2-server`, and finally
`netopeer2-cli`. If you enable debug messages for `sysrepo-plugind`, you should see that the `oven` plugin was
successfully initialized.

After you connect in CLI to the server, you are free to play with the `oven` configuration, RPCs, and notifications.
It should work like it is described in the YANG model and how would one expect an oven to behave. However, here is
one example use-case:

1.  As the very first step, subscribe to the notification `oven-ready` using the command

        subscribe --filter-xpath /oven:oven-ready

2.  Prepare food to be inserted into the oven once it reaches a temperature, which will be configured later. The oven
    is turned off by default (which you can verify by executing `get-config --source running --defaults report-all`). In
    NETCONF terms, you execute the `insert-food` RPC. You do that using `netopeer2-cli` with the command

        user-rpc

    and input

        <insert-food xmlns="urn:sysrepo:oven">
            <time>on-oven-ready</time>
        </insert-food>

    as the RPC content. You should receive OK from the server.

3.  Now you are going to turn the oven on and expect to be notified once it reaches the configured temperature. Also, the food
    should be inserted at that moment. So, you execute

        edit-config --target running --config

    with the content

        <oven xmlns="urn:sysrepo:oven">
            <turned-on>true</turned-on>
            <temperature>200</temperature>
        </oven>

4.  After ~4 seconds you should receive the notification. You can also verify that everything went correctly with

        get --filter-xpath /oven:*

    The food should be inside the oven.

5.  Once you think the food is baked just right, remove it with another RPC by

        user-rpc

    with

        <remove-food xmlns="urn:sysrepo:oven"/>

    Bon appetit!

@section daemon Oven Daemon

If you want your device to have a stand-alone daemon that will run as a separate process and not using
`sysrepo-plugind` then you do not need to develop a plugin. Having a separate daemon actually has only a few major differences
that have been mentioned in the previous sentence.

As for the code itself, no specific functions are required as the code will not be compiled into a shared library but
an executable binary. However, if the plugin is to be transformed into an application nothing prevents reusing the
whole code.

All that is needed is a main function that will call sr_plugin_init_cb() at the beginning and
sr_plugin_cleanup_cb() before terminating. In addition, these functions need a Sysrepo session. In order to create
a session we first need a connection. So, first a connection is created with sr_connect() and if successful,
a session created with sr_session_start(). Now, it is possible to call the **init** function and on daemon termination
to properly cleanup by both calling the **cleanup** function and freeing the session and connection. Additionally, before
being able to compile such an application, the printing macros would have to be changed as there will no longer be
a master daemon that would handle printing messages. After these changes, the `oven` daemon should be ready.

*/
