Introduction
In this tutorial, we will cover the basic functions in hpd, which are needed in the main program, adapters, and applications.
The full files can be found in the example/demo folder, and can be compiled with make example_demo
.
Main Program
The main program (demo_main.c) is pretty similar to that of the template example. Note, that we included the hpd_rest webservice module, which are included and installed alongside hpd itself.
int main(
int argc,
char *argv[])
{
if ((rc =
hpd_alloc(&hpd)))
goto error_return;
if ((rc =
hpd_start(hpd, argc, argv)))
goto error_free;
if ((rc =
hpd_free(hpd)))
goto error_return;
return rc;
error_free:
error_return:
return rc;
}
The hpd functionality used in this file:
- hpd_daemon_api.h: Header file containing all daemon related functions.
- hpd_rest.h: Header file containing the hpd_rest module.
- hpd_error_t: An error code and most functions in hpd returns, which should be checked for errors.
- hpd_t: The type of a hpd instance.
- hpd_alloc(): Allocates memory for hpd.
- hpd_module(): Adds a module to hpd. In this case we add three; hpd_rest, a demo adapter and a demo app(lication).
- hpd_start(): Starts and runs hpd.
- hpd_free(): Frees the memory allocated with hpd_alloc().
Demo Adapter
Here, we will create a simple adapter, that creates N virtual lamps, where N is a number given as command-line argument. If N is not given we will create just one lamp as default.
Header file
As usual, the header file (demo_adapter.h) with only include a declaration of single hpd_module_def. Which describes our module, and makes it easy to include in a main program when calling hpd_module().
#ifndef HOMEPORT_DEMO_ADAPTER_H
#define HOMEPORT_DEMO_ADAPTER_H
#endif //HOMEPORT_DEMO_ADAPTER_H
Source file
Our source file (demo_adapter.c) will use two header files.
- hpd_adapter_api.h: All hpd functions required to make an adapter.
- hpd_common.h: A set of basic defines to ease simple tasks, such as memory allocation.
We will need two struct of our own for this adapter:
- demo_adapter_t: Will be used to store global data for our adapter, such as the number of lamps. We also store the unique id of the module (as given to hpd_module() in the main program), a reference to the adapter and the context.
- demo_adapter_srv_t: Used to store data related to a single service in the adapter. This holds the current state of the lamp and a pointer the global data.
As in the template we provide the five functions and an instance of the module definition struct, as declared in the header file:
Function Implementations
The order of the functions here differs from the actual file (demo_adapter.c), so refer to the file if you need to compile this example.
on_create() and on_destroy()
demo_adapter_on_create() first allocation the global memory and initialises it, then it add the support command-line arguments to hpd:
{
goto error_free;
if ((rc =
hpd_module_add_option(context,
"num-lamps",
"count", 0,
"Number of lamps to create. Default 1.")))
goto error_free;
*data = demo_adapter;
alloc_error:
error_free:
return rc;
}
New functionality introduced:
demo_adapter_on_destroy() frees this memory:
on_parse_opt()
demo_adapter_on_parse_opt() checks for the argument we defined with hpd_module_add_option() earlier and sets num_lamps accordingly. Recall that this function should return HPD_E_ARGUMENT, when the argument is not recognised.
{
if (strcmp(name, "num-lamps") == 0) {
}
}
on_start() and on_stop()
demo_adapter_on_start() creates the adapter object and the required number of lamps (we will make demo_adapter_create_adapter() and demo_adapter_create_lamp() in a bit). On errors it stops everything again:
{
char *id = NULL;
for (
int i = 0; i < demo_adapter->
num_lamps; i++) {
}
alloc_error:
snprintf_error:
error_free_id:
error_return:
return rc;
}
New functionality introduced:
demo_adapter_on_stop() stops anything that was started during runtime. As we only created one adapter, we only have to detach and free this - everything underneath it (devices, services, and parameters) will automatically be freed by hpd:
{
error_free_id:
error_return:
return rc;
}
New functionality introduced:
Object creation
In demo_adapter_on_start() we called two functions; demo_adapter_create_adapter() and demo_adapter_create_lamp() to create our object models, which we will implement now.
First demo_adapter_create_adapter() allocates an adapter object, attaches it and creates an id reference to it. As unique id we will use the module id, because we do not have better options and we are only making one adapter per module.
{
error_free_adapter:
error_free_id:
error_return:
return rc;
}
New functionality introduced:
- hpd_adapter_id_alloc(): Allocate a new id reference. Not it is perfectly fine to do this, even when the adapter object does not exist. We will however be getting HPD_E_NOT_FOUND if we tried to used it for anything.
- hpd_adapter_alloc(): Allocate a new adapter object. Note, this may return HPD_E_NOT_UNIQUE, if the given id is not globally unique. In this case the adapter will fail in such cases (by returning the error).
- hpd_adapter_set_attr(): Sets an attribute on the adapter, in this case we set the default type attribute (HPD_ATTR_TYPE). If you have to set more than one attribute, you may prefer hpd_adapter_set_attrs() instead that takes a variable number of arguments.
- hpd_adapter_attach(): Attach the adapter to hpd. Note that, after this the adapter instance of hpd_adapter_t, will be invalid and can no longer be used. Use hpd_adapter_detach() to obtain it again.
demo_adapter_create_lamp() create the device object and calls demo_adapter_create_service() to create the service and parameter. Note that we are creating the service before attaching the device - unlike adapters, devices needs to be attached with all their services and parameters already attached.
{
error_free:
error_return:
return rc;
}
New functionality introduced:
demo_adapter_create_service() creates the service, the struct for our custom data for it, and calls demo_adapter_create_parameter() to create the parameter. We will create two callbacks demo_adapter_on_get() and demo_adapter_on_put() later.
{
alloc_error:
error_free_data:
error_free_service:
error_return:
return rc;
}
New functionality introduced:
- hpd_service_alloc(), hpd_service_attach(), and hpd_service_free(): Like the equivalent functions for devices and adapters.
- hpd_service_set_actions(): Set action on a service, we set both a HPD_M_GET and a HPD_M_PUT action. Here, we used the plural version for setting multiple at once, in which case HPD_M_NONE should always come last. If you only have to set one action, have a look at hpd_service_set_action().
- hpd_service_set_data(): Used to set custom data on a service, in this case an instance of demo_adapter_srv_t. The given on_free function will be used by hpd when this data needs to be freed later (can be NULL). In this case we used the standard C free() function.
demo_adapter_create_parameter() creates a new parameter object for our service:
{
error_free:
error_return:
return rc;
}
New functionality introduced:
Action Callbacks
demo_adapter_on_get() will send the current value of the lamp:
demo_adapter_on_put() will set the value of the lamp and then send the new current value:
New functionality introduced:
- hpd_status_t: Both functions returns hpd_status_t, which is a HTTP status code that will be sent to the requester. In cases where we handle sending the response later, we need to return HPD_S_NONE. Note that it is perfectly fine to keep the hpd_request_t pointer for use later, when we return HPD_S_NONE. However, we do commit to calling hpd_respond() some time later in those cases.
Sending and Setting Values
demo_adapter_send_value() sends a response to the requester with the current value:
{
error_free_val:
error_free_res:
error_return:
return HPD_S_500;
}
New functionality introduced:
- hpd_response_t: The type of a response.
- hpd_response_alloc(): Allocate a new response instance.
- hpd_value_t: The type of a value.
- hpd_value_allocf(): One of several functions to allocate a new value instance. This one have similar behaviour as the printf family of functions.
- hpd_response_set_value(): Set the value of a response. After this, the value is no longer valid to use.
- hpd_respond(): Sends the response to the requester. After this, the response is no longer valid to use, and we have to return HPD_S_NONE, because to already responded to the request ourselves.
- hpd_value_free(): Frees a value.
- hpd_response_free(): Frees a response.
demo_adapter_set_value() set the value of a lamp, which was store in the state field of demo_adapter_srv_t. To do so we first have to retrieve the new value from the request and parse it from a string into an integer. If the value is changed be call demo_adapter_send_changed() to send out an event.
{
const char *body;
size_t len;
char *nul_term = NULL;
int state = atoi(nul_term);
if (state != srv_data->
state) {
}
alloc_error:
return HPD_S_500;
error_return:
return HPD_S_500;
}
New functionality introduced:
demo_adapter_send_changed() sends out events that the value of changed to any possible listeners.
New functionality introduced:
- hpd_changed(): Informs hpd that a service changed value. After this, the value is no longer valid to use.