|
Friendly LWM2M client
|
This section describes the general structure of Friendly LWM2M client, the structure and purpose of its components, user interfaces and features of use.
Friendly LWM2M client, also known as Wpp, is a project built upon the Wakaama project, which implements the Lightweight Machine to Machine (LwM2M) protocol. The architecture of Wpp is carefully designed to provide a high-level presentation of Wakaama functionalities while retaining control and flexibility.
Since Wpp is built upon the Wakaama, the existing interfaces and capabilities of Wakaama affect the internal structure of Wpp. For a better understanding of what components should be present in Wpp, the Wakaama interface was segregated, based on it, the necessary components for Wpp were created.
Wpp's architecture is composed of several key components, each serving a distinct role:
The diagram below shows the common Wpp components with their dependencies on Wakaama. A part of the Wakaama library with its components is highlighted in yellow, and a part of the Wpp library is highlighted in green. The dependencies of the environment and the user program from the Wpp library are also indicated.
In the library, this structure has the following representation.
The main interface of the library consists of two main parts:
The State Management interface is presented in the form of a single class wpp::WppClient, which provides a single access point for processing the internal state of the client and the object registry. This approach allows to synchronize access to internal state changes when using the client by different threads. To create a client, the user must call the wpp::WppClient::create() method, as parameters this method accepts a structure with information about the client, through which user can set its name wpp::WppClient::ClientInfo::endpointName, the second parameter is an object of class wpp::WppConnection that is implemented by the user.
Synchronization of access to the client is provided by the ownership mechanism, the client can be owned by only one program subject at a time, for this purpose a pair of methods wpp::WppClient::takeOwnership() and wpp::WppClient::giveOwnership() are used. Any actions with the client can be performed only after the wpp::WppClient::takeOwnership() method returns a valid object. After completing the manipulations with the client, the owner must return control over the client by calling the wpp::WppClient::giveOwnership() method. wpp::WppClient has two types of methods to take ownership wpp::WppClient::takeOwnership() and wpp::WppClient::takeOwnershipBlocking(), the first method works without any blocking, if the client is already owned, it returns an empty pointer, but wpp::WppClient::takeOwnershipBlocking() works differently, if the client is owned, it blocks its call until the client is released after which takes ownership of the client and returns a valid object.
The second important part of wpp::WppClient is wpp::WppClient::loop() method for state processing, which when called performs three main tasks: processing packets received from the server, processing tasks from wpp::WppTaskQueue, handling the internal state of the Wakaama library. After completion, the wpp::WppClient::loop() method returns the maximum time in seconds after which the user must call this method the next time. But if after calling wpp::WppClient::loop() the user creates a task wpp::WppTaskQueue which must be called earlier than the next call of the method wpp::WppClient::loop() , then in this case, the wpp::WppClient::loop() method must be executed earlier than scheduled for the timely execution of the user request. It should be noted that the time returned after calling wpp::WppClient::loop() is the maximum amount of time after which this method should be executed, while the minimum threshold is not set. Therefore, the user can call this method even without delays between calls. It should also be noted that the next call is timed in seconds, so there is no need to call wpp::WppClient::loop() more often than once per second, although this is acceptable. It is worth noting that the wpp::WppClient::loop() method can be called only after the user takes ownership of wpp::WppClient.
To access the registry, the client has a method wpp::WppClient::registry() that returns a registry object. Other methods and their purpose are described in the Code section at the following link wpp::WppClient.
The Registry management interface consists of one class wpp::WppRegistry, which represents the registry of all existing objects in the client. Each object that exists in the client must be represented as a separate method in wpp::WppRegistry, for example, for the object wpp::Device there is a method wpp::WppRegistry::device() which when called, returns the corresponding object. User can also use another way to get the required object through wpp::OBJ_ID, for this needs to call the wpp::WppRegistry::object() method and pass the ID of the corresponding object. Each existing object in the client has its own unique ID, which is reflected in the wpp::OBJ_ID enumeration. In addition to the fact that wpp::WppRegistry stores all objects and provides access to them, it also has a function that allows to register the corresponding objects on the server, this is a pair of methods wpp::WppRegistry::registerObj() and wpp::WppRegistry::deregisterObj(), there are also methods that allows to get the current state of the object, more details at the following link wpp::WppRegistry. wpp::WppRegistry was designed in such a way that it can be used together with a set of utilities for generating objects object_maker, so it is important not to disturb the internal structure and comments that exist in wpp::WppRegistry, and if it is necessary to do this, then it must be coordinated with utilities.
The Object Management interface consists of four classes wpp::Object, wpp::ObjectImpl, wpp::Instance, wpp::Resource, but the user can interact with the interfaces of only two wpp::Object, wpp::Instance. The above classes represent the LwM2M data model of the object, which is described in the documentation 7.1. Resource Model. The general hierarchy is as follows: LwM2M has a registry with a description of each object, its resources and business logic Object and Resource Registry, but this representation is only in the form of text, not structure and code, therefore Wpp has its own representation of the registry in the form of the wpp::WppRegistry class, which stores instances of the data model that represents LwM2M objects. The mentioned data model contains wpp::Object, each object is a container for instances of its type wpp::Instance, each instance stores data specific to a certain object, these data are called resources wpp::Resource, resources are structures that display fields and object values according to the LwM2M description.
For a better understanding of the structure, it is worth describing each component separately:
The Platform Dependencies interface consists of three classes wpp::WppConnection, wpp::WppPlatform, wpp::WppGuard. Each of these classes is an interface that must be implemented by the user. An example of the implementation can be found here. The wpp::WppGuard class is responsible for providing synchronization mechanisms for accessing shared resources. It provides methods to lock, unlock, and try_lock the guard object. The user must implement these methods according to their specific synchronization requirements. wpp::WppPlatform uses the Linker Callback pattern the same as wpp::WppGuard, where the implementation of methods is carried out on the user side. It provides functions for retrieving the elapsed time, printing messages, and simplifying the usage of the print function. wpp::WppConnection provides an abstract interface for establishing and managing connections in the Wpp. It defines pure virtual methods that must be implemented by the user to establish a connection, disconnect, compare session identifiers, and send packets over the connection. The wpp::WppConnection class also provides methods for adding packets to a queue, retrieving the size of the packet queue, clearing the packet queue, and setting/getting the COAP block size.
Wpp Task Queue tool for planning and executing tasks in the context of wpp::WppClient. As described in the section **State Management** the client can be owned by only one program subject at a time, but what if it is necessary to make changes to the registry when the registry is already owned? You can call the wpp::WppClient::takeOwnershipBlocking() method, but in this case the execution of the thread will be blocked until the client is released, but if blocking or any waiting is not allowed, then you can schedule a task in the context wpp::WppClient. This is only one of the problems that wpp::WppTaskQueue solves, also its functionality allows to schedule cyclic tasks, or tasks that must be performed before a specific condition occurs. A more detailed description of its capabilities and use can be found at the following link wpp::WppTaskQueue. It is worth noting several features of the wpp::WppTaskQueue implementation for a better understanding of its operation. The created tasks are processed all together sequentially, at the time of calling wpp::WppClient::loop(), so the frequency of calling tasks depends on the frequency of calling the wpp::WppClient::loop() method, but this does not mean, that wpp::WppClient::loop() must be called continuously because the granularity of deferred task invocation is measured in seconds. wpp::WppTaskQueue does not guarantee that the task will be called on time, the only guarantee is that the tasks will not be called before the specified time. Each task is called in the context of wpp::WppClient, which means that the user cannot make changes to the registry at this time, the executed task is actually the sole owner of the registry. From the context of the task, you can safely use the wpp::WppTaskQueue interface to create new tasks, delete them, and get the status. If a task is created from the context of an executed task or during task processing, then it can be executed only at the next call to wpp::WppClient::loop(), if a request was created to delete a task that is currently being executed, then the removal process will only happen after the execution is complete. If another task is deleted from the execution environment or elsewhere, and it has not yet been executed, then it will be deleted without execution. The task that is being executed must not contain delays or blocks in its code, as this will delay the execution of all other tasks and the processing of the client's state.
Wpp's architecture allows for easy integration into new environments and the possibility to implementation Platform dependent interface as per environmental requirements. This flexibility is attributed to the absence of external dependencies, as Wpp defines all input and output interfaces, and the environment implements them. For integration to new environment user should implement the next interfaces: wpp::WppConnection, wpp::WppPlatform, wpp::WppGuard. Example can be found in the platform folder.
As mentioned in the section Registry Management, for Wpp a complex of utilities object_maker has been developed for generation, integration, deletion and update of objects in the form of source code. object_maker is not a critically important part of Wpp without which the creation of new objects is impossible, it is more of an auxiliary character to simplify tasks related to the modification of the object registry. Let's consider in more detail the capabilities of object_maker:
The architecture of Friendly LWM2M client (Wpp) is a well-structured framework that provides a robust platform for the development and implementation of LwM2M client applications. It maintains a balance between high-level functionality and control, and is designed to be flexible and adaptable to various user and environmental needs.