8.1.3. How to Use the Database Module

This How-to shows how to add new entries to the database, and how to read and write database entries.

8.1.3.1. How to Add a Database Entry?

Four steps need to be undertaken to add an entry to the database.

First, an identification number (ID) for this database entry must be added to enum DATA_BLOCK_ID_e in file src/app/engine/config/database_cfg.h. This enumeration value respectively the identification number functions as uniqueId for each database entry to synchronize database entries with local copies during read and write access to the database.

Note

The new ID must start with DATA_BLOCK_ID_. Before any source is compiled the build process checks that a database variable is initialized with the matching ID from DATA_BLOCK_ID_e. For the details of the checker implementation see Checking Database Entries.

In this example the name of the new database entry is example, therefore the corresponding ID must be the prefix and the name in uppercase, which gives DATA_BLOCK_ID_EXAMPLE.

Listing 8.1 Adding a new element example to the DATA_BLOCK_ID_e enum
1
2
3
4
5
6
7
8
9
 /** data block identification numbers */
 typedef enum DATA_BLOCK_ID {
     DATA_BLOCK_ID_EXAMPLE, /**< some example database entry */
     /* ...
        ...
        ...
     */
     DATA_BLOCK_ID_MAX, /**< DO NOT CHANGE, MUST BE THE LAST ENTRY */
 } DATA_BLOCK_ID_e;

Second, a struct definition of the database entry has to be added in file src/app/engine/config/database_cfg.h. This structure needs to contain a variable header of type DATA_BLOCK_HEADER_s at the beginning of the struct. This header contains a uniqueID for this database entry as well as timestamps. These timestamps (timestamp and previousTimestamp) are automatically updated each time new data is stored in this database entry. The remaining struct consists of all the data needed for the entry.

Note

The new database entry must start with DATA_BLOCK_. Before any source is compiled the build process checks that a database variable is initialized with the matching ID from DATA_BLOCK_ID_e. For the details of the checker implementation see Checking Database Entries.

Again, the name of the new database entry is example, therefore the corresponding data block must be the prefix followed by the name in uppercase and the _s suffix as the typedef is a struct, which gives DATA_BLOCK_EXAMPLE_s.

Listing 8.2 Creating a new database struct
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 /**
 * data block struct of example
 */
 typedef struct DATA_BLOCK_EXAMPLE {
     /* This struct needs to be at the beginning of every database entry. During
     * the initialization of a database struct, uniqueId must be set to the
     * respective database entry representation in enum DATA_BLOCK_ID_e. */
     DATA_BLOCK_HEADER_s header;
     uint16_t dummyValue;
 } DATA_BLOCK_EXAMPLE_s;

The database checker basically checks that if a database struct is found (something matching DATA_BLOCK_(A_Z_0-9)_s) that it is initialized with a corresponding ID name.

Examples:

Struct

Id

DATA_BLOCK_**EXAMPLE**_s

DATA_BLOCK_ID_**EXAMPLE**

DATA_BLOCK_**CELL_VOLTAGE**_s

DATA_BLOCK_ID_**CELL_VOLTAGE**

Third, in src/app/engine/config/database_cfg.c, a static variable with the structure type must be declared. In this declaration the uniqueId of the database header must be initialized with the identification number added in the first step. This structure data_blockExample contains the actual values of this database entry during once the BMS is running.

Listing 8.3 Initialization of the static variable inside the database module
1
 static DATA_BLOCK_EXAMPLE_s data_blockExample = {.header.uniqueId = DATA_BLOCK_ID_EXAMPLE};

Last, this declared struct must be linked to the actual database implementation by adding an additional entry to the structure DATA_BASE_s data_database[] in file src/app/engine/config/database_cfg.c

Listing 8.4 Adding the struct to the database
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 /**
 * @brief   channel configuration of database (data blocks)
 * @details all data block managed by database are listed here (address, size,
 *          consistency type)
 */
 DATA_BASE_s data_database[] = {
     {
         (void*)(&data_blockExample),
         sizeof(DATA_BLOCK_EXAMPLE_s)
     },
     /* ...
        ...
        ...
     */
 };

8.1.3.2. How to Read a Database Entry?

Data inside the database can not be accessed directly. The data from a database entry has to be copied inside a local representation of the requested entry. For the local structure of the database entry, the uniqueId of the database entry header has to be initialized with the identification number of the requested database entry. Once this is done, a database entry can simply be accessed by calling function DATA_READ_DATA() and passing the pointer to the local database entry structure.

Listing 8.5 Reading a database entry
1
2
3
4
 /* Create local structure of database entry */
 DATA_BLOCK_EXAMPLE_s tableLocalExampleEntry = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE };
 /* Read data from database entry and update local variable */
 DATA_READ_DATA(&tableLocalExampleEntry);

Up to four database entries can be accessed on the same time with one call of DATA_READ_DATA().

Listing 8.6 Reading multiple database entries
1
2
3
4
5
6
 DATA_BLOCK_EXAMPLE_0_s tableLocalExampleEntry0 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_0 };
 DATA_BLOCK_EXAMPLE_1_s tableLocalExampleEntry1 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_1 };
 DATA_BLOCK_EXAMPLE_2_s tableLocalExampleEntry2 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_2 };
 DATA_BLOCK_EXAMPLE_3_s tableLocalExampleEntry3 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_3 };
 /* Read data from 4 database entries and update local variables */
 DATA_READ_DATA(&tableLocalExampleEntry0, &tableLocalExampleEntry1, &tableLocalExampleEntry2, &tableLocalExampleEntry3);

8.1.3.3. How to Write a Database Entry?

It is not possible to directly write data into a database entry. Only the content of a local representation of the requested database entry can be copied into the database. The uniqueId of the database entry header has to be initialized with the identification number of the requested database entry for the local structure of the database entry. Once this is done, a database entry can be written by calling function DATA_WRITE_DATA() and passing the pointer to the local database entry structure. Up to four database entries can be written with one call of DATA_WRITE_DATA().

Listing 8.7 Writing a database entry
1
2
3
4
5
6
 /* Create local structure of database entry */
 DATA_BLOCK_EXAMPLE_s tableLocalExampleEntry = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE };
 /* Set dummy value to 100 */
 tableLocalExampleEntry.dummyValue = 100;
 /* Update database entry with changed dummy value */
 DATA_WRITE_DATA(&tableLocalExampleEntry);

After a call of DATA_WRITE_DATA the timestamp variables in the database entry header are automatically updated database entry AND the local database entry structure.

Up to four database entries can be written on the same time with one call of DATA_WRITE_DATA().

Listing 8.8 Writing multiple database entries
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 DATA_BLOCK_EXAMPLE_0_s tableLocalExampleEntry0 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_0 };
 DATA_BLOCK_EXAMPLE_1_s tableLocalExampleEntry1 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_1 };
 DATA_BLOCK_EXAMPLE_2_s tableLocalExampleEntry2 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_2 };
 DATA_BLOCK_EXAMPLE_3_s tableLocalExampleEntry3 = { .header.uniqueId = DATA_BLOCK_ID_EXAMPLE_3 };
 /* Set dummy values to 100 */
 tableLocalExampleEntry0.dummyValue = 100;
 tableLocalExampleEntry1.dummyValue = 100;
 tableLocalExampleEntry2.dummyValue = 100;
 tableLocalExampleEntry3.dummyValue = 100;
 /* Update database entries with changed dummy values */
 DATA_WRITE_DATA(&tableLocalExampleEntry0, &tableLocalExampleEntry1, &tableLocalExampleEntry2, &tableLocalExampleEntry3);

8.1.3.4. Further Reading

Implementation details of the database module are found in Database Module.