OpenThread_app
Loading...
Searching...
No Matches
Architecture and Design Patterns

Introduction

This document provides a detailed analysis of the OTApp Core Framework framework software architecture, with particular emphasis on the implementation of design patterns and Real-Time Operating System (RTOS) engineering techniques. The description focuses on specific mechanisms applied in the source code to ensure modularity, scalability, and thread safety.

1. Design Patterns

1.1. Singleton

Implementation: The Singleton pattern is utilized to centralize the management of key system resources, such as the OpenThread stack instance and the main device driver. The implementation relies on static pointers within modules, which are accessible exclusively via dedicated accessors (getters). This guarantees the existence of exactly one controller instance throughout the application lifecycle and protects against global state inconsistency.

Code Example: The ot_app.c module encapsulates the openThreadInstance pointer, exposing it to the rest of the system via the otapp_getOpenThreadInstancePtr() function.

// File: ot_app.c
static otInstance *openThreadInstance;
// Private instance
// Global Access Point
{
return openThreadInstance;
}
otInstance * otapp_getOpenThreadInstancePtr(void)
Retrieves the global OpenThread instance pointer.
Definition ot_app.c:116
struct otInstance otInstance
Definition mock_ip6.h:20

An analogous mechanism is applied for the driver in ot_app_drv.c:

// File: ot_app_drv.c
static ot_app_devDrv_t ot_app_devDrv = { ... };
// Static initialization
{
return &ot_app_devDrv;
}
ot_app_devDrv_t * ot_app_drv_getInstance(void)
Definition ot_app_drv.c:73
Definition ot_app_drv.h:242

1.2. Observer

Implementation: The Observer mechanism realizes loose coupling between the networking layer and business logic. It is applied in two critical areas:

  1. Pairing Event Propagation: Notifying registered callbacks about changes in network topology (e.g., adding a new device).
  2. CoAP Observe Handling (RFC 7641): The CoAP server implementation maintains a list of subscribers for a given resource. Upon a resource state change, the ot_app_coap_uri_obs.c module iterates through the list of active observers, generating asynchronous notifications without the need for client-side polling.

Code Example (CoAP Notify):

// File: ot_app_coap_uri_obs.c
int8_t oac_uri_obs_notify(oac_uri_observer_t *subListHandle, const otIp6Address *excludedIpAddr, oacu_uriIndex_t uriIndex, const uint8_t *dataToNotify, uint16_t dataSize)
{
// ...
for(uint8_t i = 0; i < OAC_URI_OBS_SUBSCRIBERS_MAX_NUM; i++)
{
if(oac_uri_obs_spaceDevNameIsTaken(subListHandle, i))
{
// ... check if specific URI is subscribed ...
// send data to subscriber
otapp_coapSendPutUri_subscribed_uris(&subListHandle[i].ipAddr, oac_txRxBuffer, sizeof(oac_txRxBuffer));
numOfnotifications++;
}
}
return numOfnotifications;
}
uint8_t oacu_uriIndex_t
Definition ot_app_coap_uri_obs.h:52
int8_t oac_uri_obs_notify(oac_uri_observer_t *subListHandle, const otIp6Address *excludedIpAddr, oacu_uriIndex_t uriIndex, const uint8_t *dataToNotify, uint16_t dataSize)
todo
Definition ot_app_coap_uri_obs.c:599
#define OAC_URI_OBS_SUBSCRIBERS_MAX_NUM
Definition ot_app_coap_uri_obs.h:39
void otapp_coapSendPutUri_subscribed_uris(const otIp6Address *ipAddr, const uint8_t *data, uint16_t dataSize)
Sends a PUT request to update a resource on a remote device.
Definition ot_app_coap.c:361
PRIVATE int8_t oac_uri_obs_spaceDevNameIsTaken(oac_uri_observer_t *subListHandle, int8_t tabDevId)
Definition ot_app_coap_uri_obs.c:50
Definition ot_app_coap_uri_obs.h:82
Definition mock_ip6.h:77

1.3. Strategy / Interface

Implementation: The ot_app_devDrv_t structure implements the Strategy pattern through the use of function pointers. It acts as a Hardware Abstraction Layer (HAL) and logic abstraction layer, allowing the injection of specific behavioral implementations (e.g., pairing rules, URI mapping) into the generic framework engine. This ensures the core application engine remains immutable, while new functionality is delivered via driver structure configuration during initialization.

Code Example:

// File: ot_app_drv.c
static ot_app_devDrv_t ot_app_devDrv = {
.obs_subscribedUri_clb = NULL,
.obs_pairedDevice_clb = NULL,
.pairRuleGetList_clb = NULL, // Injected pairing rule strategy
.uriGetList_clb = NULL, // Injected URI list
// ...
.api.coap = {
// ...
},
};
void otapp_coap_clientSendPutByte(const otIp6Address *peer_addr, const char *aUriPath, const uint8_t *payloadMsg, const uint16_t payloadMsgSize, otCoapResponseHandler responseHandler, void *aContext)
send coap message bytes using the PUT method
Definition ot_app_coap.c:309
#define NULL
Definition hro_utils.h:73

1.4. Facade

Implementation: The ot_app_nvs.c module serves as a Facade for the system's NVS (Non-Volatile Storage) library. It abstracts the complexity of partition initialization, handle management, and low-level error handling, exposing a simplified, atomic interface to higher layers for reading and writing configuration data.

Code Example:

// File: ot_app_nvs.c
// Simple facade hiding nvs_open, nvs_set_str, nvs_commit
int8_t ot_app_nvs_saveString(const char *inData, const uint8_t keyId)
{
// ... key handling ...
err = nvs_set_str(nvsHandle, keyName, inData);
// ... error handling ...
err = nvs_commit(nvsHandle);
// ...
}
int8_t ot_app_nvs_saveString(const char *inData, const uint8_t keyId)
Saves a string to non-volatile storage.

1.5. Command / Dispatcher

Implementation: CoAP network request handling is implemented based on a Dispatch Table. The static associative array otapp_coap_uriDefault maps resource paths (URIs) directly to handler function pointers. This mechanism eliminates extensive conditional logic, ensuring deterministic lookup time for the appropriate procedure upon an incoming request.

Code Example:

// File: ot_app_coap.c
static otapp_coap_uri_t otapp_coap_uriDefault[] ={
// ...
};
void ad_temp_uri_well_knownCoreHandle(void *aContext, otMessage *request, const otMessageInfo *aMessageInfo)
Definition ot_app_coap_uri.c:90
void otapp_coap_uri_paringServicesHandle(void *aContext, otMessage *request, const otMessageInfo *aMessageInfo)
Handler for Pairing and Discovery services (e.g., "pairing/service").
Definition ot_app_coap_uri.c:67
@ OTAPP_URI_WELL_KNOWN_CORE
Definition ot_app_coap.h:55
@ OTAPP_URI_PARING_SERVICES
Definition ot_app_coap.h:56
Definition ot_app_coap.h:66

2. Programming and System Techniques

2.1. Producer-Consumer Model (RTOS Queue)

Implementation: To separate the time-critical network context from blocking operations (e.g., flash memory writes), an asynchronous Producer-Consumer model based on a FreeRTOS queue is employed.

  • Producer: OpenThread network callbacks place pairing requests into the otapp_pair_queueHandle queue.
  • Consumer: A dedicated thread (Task) otapp_pair_task processes requests in the background, ensuring the fluidity of the network stack.

Code Example:

Producer (adding to queue):

// File: ot_app_pair.c
{
// ...
if(xQueueSend(otapp_pair_queueHandle, (void *)queueItem, (TickType_t) 0) != pdTRUE)
{
}
return OTAPP_PAIR_OK;
}
#define OTAPP_PAIR_ERROR
Generic error.
Definition ot_app_pair.h:51
#define OTAPP_PAIR_OK
Operation successful.
Definition ot_app_pair.h:48
int8_t otapp_pair_addToQueue(otapp_pair_queueItem_t *queueItem)
Adds an item to the pairing processing queue.
Definition ot_app_pair.c:101
#define xQueueSend
Definition mock_freertos_queue.h:28
#define pdTRUE
Definition mock_freertos_queue.h:34
#define TickType_t
Definition mock_freertos_semaphore.h:23
PRIVATE otapp_pair_queueItem_t queueItem
Definition ot_app_dns.c:31
Structure for the pairing event queue.
Definition ot_app_pair.h:143

Consumer (processing in task loop):

// File: ot_app_pair.c
void otapp_pair_task(void *params)
{
while (1)
{
if (xQueueReceive(otapp_pair_queueHandle, &otapp_pair_queueIteam, portMAX_DELAY) == pdTRUE)
{
// Queue item processing logic
if (otapp_pair_queueIteam.type == OTAPP_PAIR_CHECK_AND_ADD_TO_DEV_LIST)
{
// ...
}
}
}
}
@ OTAPP_PAIR_CHECK_AND_ADD_TO_DEV_LIST
Event: Check rules and potentially add device.
Definition ot_app_pair.h:137
#define xQueueReceive
Definition mock_freertos_queue.h:29
#define portMAX_DELAY
Definition mock_freertos_queue.h:35
void otapp_pair_task(void *params)
Definition ot_app_pair.c:892

2.2. Table-Driven Design

Implementation: Control logic and static data mappings (e.g., error codes, message definitions) have been delegated to data structures (tables) rather than being embedded in procedural code. This approach increases readability, facilitates text localization, and simplifies system extension with new definitions without interfering with processing logic.

Code Example: Mapping message identifiers to strings:

// File: ot_app_coap.c
static const otapp_coap_message_t otapp_coap_messages[] = {
{OTAPP_MESSAGE_ERROR, "ERROR"},
{OTAPP_MESSAGE_TEST, "Hello coap !!"},
};
{
for (uint16_t i = 0; i < OTAPP_COAP_MESSAGE_SIZE; i++)
{
if(otapp_coap_messages[i].msgID == msgID)
{
return otapp_coap_messages[i].message;
}
}
return NULL;
}
otapp_coap_messageId_t
Definition ot_app_coap.h:71
const char * otapp_coap_getMessage(otapp_coap_messageId_t msgID)
Converts an internal message ID/Type to a string representation.
Definition ot_app_coap.c:65
@ OTAPP_MESSAGE_TEST
Definition ot_app_coap.h:74
@ OTAPP_MESSAGE_ERROR
Definition ot_app_coap.h:73
@ OTAPP_MESSAGE_OK
Definition ot_app_coap.h:72
#define OTAPP_COAP_MESSAGE_SIZE
Definition ot_app_coap.c:62
Definition ot_app_coap.c:52
char * message
Definition ot_app_coap.c:54

2.3. Shared Resource Protection (Mutex)

Implementation: In the multi-threaded RTOS environment, access to shared memory buffers (e.g., otapp_charBuf) is protected by Mutex semaphores. This prevents race conditions and data corruption during simultaneous access from different tasks or interrupts.

Code Example:

// File: ot_app.c
static char otapp_charBuf[OTAPP_CHAR_BUFFER_SIZE];
static SemaphoreHandle_t otapp_mutexBuf;
char *otapp_charBufGet_withMutex()
{
// Wait for resource access (take mutex)
if(xSemaphoreTake(otapp_mutexBuf, portMAX_DELAY) == pdTRUE)
{
return otapp_charBuf;
}
return NULL;
}
void otapp_charBufRelease()
{
// Release resource (give mutex)
xSemaphoreGive(otapp_mutexBuf);
}
#define OTAPP_CHAR_BUFFER_SIZE
Size for temporary string buffers.
Definition ot_app.h:60
void * SemaphoreHandle_t
Definition mock_freertos_semaphore.h:6
#define xSemaphoreGive(sem)
Definition mock_freertos_semaphore.h:8

2.4. Encapsulation (Information Hiding)

Implementation: Code modularity in C is enforced through strict usage of the static keyword. Variables storing internal module state (e.g., the device list in ot_app_pair.c) are invisible to the rest of the system (file scope). Interaction with the module occurs exclusively through the public API defined in header files, fulfilling the principle of Information Hiding.

Code Example: Private device list inaccessible directly from other compilation units:

// File: ot_app_pair.c
// Variable visible only within this file (static)
static otapp_pair_DeviceList_t otapp_pair_DeviceList;
// Access function for other modules
{
return &otapp_pair_DeviceList;
}
otapp_pair_DeviceList_t * otapp_pair_getHandle(void)
Gets the singleton handle to the global device list.
Definition ot_app_pair.c:202
Definition ot_app_pair.c:46