1
0
mirror of https://github.com/armink/FlashDB.git synced 2025-01-16 20:12:52 +08:00

[dcos] add more documents.

This commit is contained in:
armink 2020-10-08 11:17:36 +08:00
parent e8b43b85a4
commit 599927a1a7
56 changed files with 3560 additions and 27 deletions

View File

@ -93,7 +93,7 @@ FlashDB 提供了主要功能的示例,直接加入工程即可运行,并具
## 支持
![support](docs/zh/images/wechat_support.png)
![support](docs/_media/wechat_support.png)
如果 FlashDB 解决了你的问题,不妨扫描上面二维码请我 **喝杯咖啡**~

0
docs/.nojekyll Normal file
View File

15
docs/_coverpage.md Normal file
View File

@ -0,0 +1,15 @@
![FlashDB](_media/flashdb.png)
> An ultra-lightweight embedded database.
[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB)
[![version](https://img.shields.io/github/release/armink/FlashDB.svg?style=flat-square)](https://github/license/armink/FlashDB/releases/latest)
[![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE)
- Small footprint
- Fast query speed
- Key-Value database
- Time series database
[GitHub](https://github.com/armink/FlashDB/)
[Getting started](/quick-started)

BIN
docs/_media/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/_media/flashdb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

3
docs/_navbar.md Normal file
View File

@ -0,0 +1,3 @@
- Translations
- [:cn: 中文](/zh-cn/)
- [:uk: English](/)

1
docs/_scripts/docsify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
docs/_scripts/emoji.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
docs/_scripts/prism-c.min.js vendored Normal file
View File

@ -0,0 +1 @@
Prism.languages.c=Prism.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+/,lookbehind:!0},keyword:/\b(?:__attribute__|_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,function:/[a-z_]\w*(?=\s*\()/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/,number:/(?:\b0x(?:[\da-f]+\.?[\da-f]*|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+(?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],comment:Prism.languages.c.comment,directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}},constant:/\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete Prism.languages.c.boolean;

1
docs/_scripts/search.min.js vendored Normal file

File diff suppressed because one or more lines are too long

18
docs/_sidebar.md Normal file
View File

@ -0,0 +1,18 @@
- Getting started
- [Quick Started](quick-started.md)
- Demo platforms
- [stm32f103ve](demo-stm32f103ve.md)
- [stm32f405rg](demo-stm32f405rg.md)
- [stm32f405rg-spi-flash](demo-stm32f405rg-spi-flash.md)
- Samples
- KV database
- [basic](sample-kvdb-basic.md)
- [string type](sample-kvdb-type-string.md)
- [blob type](sample-kvdb-type-blob.md)
- [KV traversal](sample-kvdb-traversal.md)
- Time series database
- [basic](sample-tsdb-basic.md)
- Development
- [porting](porting.md)
- [configuration](configuration.md)
- [API](api.md)

347
docs/api.md Normal file
View File

@ -0,0 +1,347 @@
# API description
## Blob
### Construct blob object
The process of constructing a blob object, its internal is the process of initializing and assigning values to the blob structure, writing the incoming parameters into the blob structure, and returning it
`fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)`
| Parameters | Description |
| --------- | ---------------------- |
| blob | blob initial object |
| value_buf | Buffer for storing data |
| buf_len | The size of the buffer |
| Back | The blob object after creation |
### Read blob data
Through the API of KVDB and TSDB, the blob object can be returned, and the storage address of the blob data is stored in the returned blob object. This API can read the blob data stored in the database and store it in `blob->buf`.
`size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)`
| Parameters | Description |
| ---- | -------------------------- |
| db | Database Objects |
| blob | blob object |
| Return | Length of blob data actually read |
## KVDB
### Initialize KVDB
`fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv, void *user_data)`
| Parameters | Description |
| ---------- | -------------------------------------- ------------------ |
| db | Database Objects |
| name | Database name |
| part_name | Which partition in the FAL partition table to use |
| default_kv | The default KV collection, when the first initialization, the default KV will be written to the database |
| user_data | User-defined data, NULL if not available |
| Back | Error Code |
### Control KVDB
Through the command control word, the user can perform some control operations on the database
`void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)`
| Parameters | Description |
| ---- | ---------- |
| db | Database Objects |
| cmd | Command control word |
| arg | Controlled parameters |
| Back | Error Code |
The supported command control words are as follows:
```C
#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x0 /**< Set sector size */
#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x1 /**< Get sector size */
#define FDB_KVDB_CTRL_SET_LOCK 0x2 /**< Set lock function */
#define FDB_KVDB_CTRL_SET_UNLOCK 0x3 /**< Set unlock function */
```
#### Sector size and block size
The internal storage structure of FlashDB is composed of N sectors, and each formatting takes sector as the smallest unit. A sector is usually N times the size of the Flash block. For example, the block size of Nor Flash is generally 4096.
By default, KVDB will use 1 times the block size as the sector size, that is, 4096. At this time, the KVDB cannot store a KV longer than 4096. If you want to save, for example, a KV with a length of 10K, you can use the control function to set the sector size to 12K or larger.
### Set KV
This method can be used to increase and modify KV.
- **Add**: When there is no KV with this name in KVDB, the new operation will be performed;
- **Modify**: The KV name in the input parameter exists in the current KVDB, then the KV value is modified to the value in the input parameter;
Get the corresponding value by KV's name. Support two interfaces
#### Set blob type KV
`fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)`
| Parameters | Description |
| ---- | ---------------------------- |
| db | Database Objects |
| key | KV name |
| blob | blob object, as the value of KV |
| Back | Error Code |
Example:
```C
struct fdb_blob blob;
int temp_data = 36;
/* Construct a blob object by fdb_blob_make as the value of "temp" KV */
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
```
#### Set string type KV
`fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value)`
| Parameters | Description |
| ----- | ----------- |
| db | Database Objects |
| key | KV name |
| value | KV value |
| Back | Error Code |
### Get KV
#### Get blob type KV
`size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)`
| Parameters | Description |
| ---- | ------------------------------------- |
| db | Database Objects |
| key | KV name |
| blob | Return the blob value of KV through the blob object |
| Back | Error Code |
Example:
```C
struct fdb_blob blob;
int temp_data = 0;
/* Construct a blob object to store the "temp" KV value data returned by get */
fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
/* If necessary, you can check whether blob.saved.len is greater than 0 to ensure that the get succeeds */
if (blob.saved.len > 0) {
FDB_INFO("get the 'temp' value is: %d\n", temp_data);
}
```
#### Get KV object
Unlike the `fdb_kv_get_blob` API, this API does not execute the reading of value data during the get process. The returned KV object stores the read KV attributes. This API is suitable for scenarios where the length of the value is uncertain, or the length of the value is too long, and it needs to be read in segments.
`fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv)`
| Parameters | Description |
| ---- | -------------------------------------------- ---------------- |
| db | Database Objects |
| key | KV name |
| kv | Through the KV object, return the attributes of the KV, and then use `fdb_kv_to_blob` to convert to a blob object, and then read the data |
| Back | Error Code |
#### Get string type KV
**Note**:
- This function is not allowed to be used continuously, and `strdup` should be used when using it to ensure that the memory space of the string returned each time is independent;
- This function does not support reentrancy. The returned value is located in the internal buffer of the function. For security reasons, please lock it.
`char *fdb_kv_get(fdb_kvdb_t db, const char *key)`
| Parameters | Description |
| ---- | ----------------------------------- |
| db | Database Objects |
| key | KV name |
| Return | !=NULL: KV value; NULL: Get failed |
### Delete KV
`fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key)`
| Parameters | Description |
| ---- | ---------- |
| db | Database Objects |
| key | KV name |
| Back | Error Code |
### Reset KVDB
Reset the KV in KVDB to the initial default value
`fdb_err_t fdb_kv_set_default(fdb_kvdb_t db)`
| Parameters | Description |
| ---- | ---------- |
| db | Database Objects |
| Back | Error Code |
### Print KV information in KVDB
`void fdb_kv_print(fdb_kvdb_t db)`
| Parameters | Description |
| ---- | ---------- |
| db | Database Objects |
| Back | Error Code |
### Convert KV objects to blob objects
`fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob)`
| Parameters | Description |
| ---- | ------------------ |
| kv | KV object to be converted |
| blob | blob object before conversion |
| Return | Converted blob object |
### Initialize KV iterator
`fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr)`
| Parameters | Description |
| ---- | -------------------- |
| itr | Iterator object to be initialized |
| Return | Iterator object after initialization |
### Iteration KV
Using this iterator API, all KVs in the entire KVDB can be traversed.
> **Note**: Please initialize the iterator before use
`bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr)`
[Click to view sample](sample-kvdb-traversal.md)
## TSDB
### Initialize TSDB
`fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len, void *user_data)`
| Parameters | Description |
| --------- | ------------------------------- |
| db | Database Objects |
| name | Database name |
| part_name | Which partition in the FAL partition table to use |
| get_time | Function to get the current timestamp |
| max_len | Maximum length of each TSL |
| user_data | User-defined data, NULL if not available |
| Back | Error Code |
### Control TSDB
Through the command control word, the user can perform some control operations on the database
`void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)`
| Parameters | Description |
| ---- | ---------- |
| db | Database Objects |
| cmd | Command control word |
| arg | Controlled parameters |
| Back | Error Code |
The supported command control words are as follows:
```C
#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x0 /**< Set sector size */
#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x1 /**< Get sector size */
#define FDB_TSDB_CTRL_SET_LOCK 0x2 /**< Set lock function */
#define FDB_TSDB_CTRL_SET_UNLOCK 0x3 /**< Set unlock function */
#define FDB_TSDB_CTRL_SET_ROLLOVER 0x4 /**< Set whether to roll writing, default roll. When setting non-rolling, if the database is full, no more writes can be appended */
#define FDB_TSDB_CTRL_GET_ROLLOVER 0x5 /**< Get whether to scroll to write */
#define FDB_TSDB_CTRL_GET_LAST_TIME 0x6 /**< Get the timestamp when TSL was last appended */
```
### Append TSL
For TSDB, the process of adding TSL is the process of appending a new TSL to the end of TSDB
`fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)`
| Parameters | Description |
| ---- | --------------------------- |
| db | Database Objects |
| blob | blob object, as TSL data |
| Back | Error Code |
### Iterative TSL
Traverse the entire TSDB and execute iterative callbacks
`void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg)`
| Parameters | Description |
| ------ | --------------------------------------- |
| db | Database Objects |
| cb | Callback function, which will be executed every time the TSL is traversed |
| cb_arg | Parameters of the callback function |
| Back | Error Code |
### Iterate TSL by time period
According to the time range, traverse the entire TSDB and execute iterative callbacks
`void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)`
| Parameters | Description |
| ------ | --------------------------------------- |
| db | Database Objects |
| from | Start timestamp |
| to | End timestamp |
| cb | Callback function, which will be executed every time the TSL is traversed |
| cb_arg | Parameters of the callback function |
| Back | Error Code |
### Query the number of TSL
According to the incoming time period, query the number of TSLs that meet the state
`size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)`
| Parameters | Description |
| ------ | -------------- |
| db | Database Objects |
| from | Start timestamp |
| to | End timestamp |
| status | TSL status conditions |
| Back | Quantity |
### Set TSL status
`fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)`
| Parameters | Description |
| ------ | ------------ |
| db | Database Objects |
| tsl | TSL Object |
| status | TSL's new status |
| Back | Error Code |
### Clear TSDB
`void fdb_tsl_clean(fdb_tsdb_t db)`
| Parameters | Description |
| ---- | ---------- |
| db | Database Objects |
| Back | Error Code |
### Convert TSL objects to blob objects
`fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)`
| Parameters | Description |
| ---- | ------------------ |
| tsl | TSL object to be converted |
| blob | blob object before conversion |
| Return | Converted blob object |

41
docs/configuration.md Normal file
View File

@ -0,0 +1,41 @@
# Configuration
When using FlashDB, you can configure its functions through `fdb_cfg.h`. The file template is located in the `inc` directory, or you can copy it in the specific demo project. Let's introduce the configuration details in detail below
## FDB_USING_KVDB
Enable KVDB feature
### FDB_KV_AUTO_UPDATE
Enable KV automatic upgrade function. After this function is enabled, `fdb_kvdb.ver_num` stores the version of the current database. If the version changes, it will automatically trigger an upgrade action and update the new default KV collection to the current database.
## FDB_USING_TSDB
Enable TSDB feature
## FDB_WRITE_GRAN
Flash write granularity, the unit is bit. Currently supports
- 1: nor flash
- 8: stm32f2/f4 on-chip Flash
- 32: stm32f1 on-chip Flash
If multiple Flash specifications are used in the database, for example: both nor flash and stm32f4 on-chip Flash, the maximum value is used as the configuration item, namely: 8 bit
## FDB_BIG_ENDIAN
MCU small-endian configuration, when the default is not configured, the system automatically uses the small-endian configuration
## FDB_PRINT(...)
The print function macro defines the configuration. When it is not configured by default, using `printf` as the print log is the output function. Users can also customize new print function macro definitions, for example:
```C
#define FDB_PRINT(...) my_printf(__VA_ARGS__)
```
## FDB_DEBUG_ENABLE
Enable debugging information output. When this configuration is closed, the system will not output logs for debugging.

35
docs/demo-stm32f103ve.md Normal file
View File

@ -0,0 +1,35 @@
# stm32f103ve on-chip flash demo
## What
KVDB and TSDB demo on STM32F10X chip
- MCU: STM32F103VE
- Flash Driver
- STM32 on-chip flash
- IO
- UART
- TXD: PA9
- RXD: PA10
## How
### Step1: connect the serial terminal
Connect the board to PC by serial port and open the PC serial terminal.
### Step2: open demo project
support 2 kinds of IDEs
- Keil MDK: open `RVMDK\FlashDB.uvprojx`
- [RT-Studio](https://www.rt-thread.io/studio.html): import this folder by import wizard
### Step3: build and download
Download the firmware to your board when build successful.
### Step4: check the log
This demo's log will output to PC serial.

View File

@ -0,0 +1,40 @@
# stm32f405rg demo
## What
KVDB and TSDB demo on STM32F4X chip
- MCU: STM32F405RG
- Flash Driver
- spi flash (W25Q64) by using [SFUD](https://github.com/armink/SFUD)
- IO
- UART
- TXD: PA9
- RXD: PA10
- SPI
- CS: PB12 (software CS)
- SCK: PB13
- MISO: PB14
- MOSI: PB15
## How
### Step1: connect the serial terminal
Connect the board to PC by serial port and open the PC serial terminal.
### Step2: open demo project
support 2 kinds of IDEs
- Keil MDK: open `RVMDK\FlashDB.uvprojx`
- [RT-Studio](https://www.rt-thread.io/studio.html): import this folder by import wizard
### Step3: build and download
Download the firmware to your board when build successful.
### Step4: check the log
This demo's log will output to PC serial.

35
docs/demo-stm32f405rg.md Normal file
View File

@ -0,0 +1,35 @@
# stm32f405rg on-chip flash demo
## What
KVDB and TSDB demo on STM32F4X chip
- MCU: STM32F405RG
- Flash Driver
- STM32 on-chip flash
- IO
- UART
- TXD: PA9
- RXD: PA10
## How
### Step1: connect the serial terminal
Connect the board to PC by serial port and open the PC serial terminal.
### Step2: open demo project
support 2 kinds of IDEs
- Keil MDK: open `RVMDK\FlashDB.uvprojx`
- [RT-Studio](https://www.rt-thread.io/studio.html): import this folder by import wizard
### Step3: build and download
Download the firmware to your board when build successful.
### Step4: check the log
This demo's log will output to PC serial.

View File

@ -1 +0,0 @@
# Coming soon...

View File

@ -1,3 +0,0 @@
|File name |Description|
|:----- |:----|
|api.md |API description|

65
docs/index.html Normal file
View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<title>FlashDB</title>
<meta name="description" content="An ultra-lightweight embedded database.">
<link rel="icon" href="_media/favicon.ico">
<link rel="stylesheet" href="vue.css">
</head>
<body>
<div id="app">Loading ...</div>
<script>
window.$docsify = {
auto2top: true,
executeScript: true,
loadSidebar: true,
loadNavbar: true,
autoHeader: true,
mergeNavbar: true,
maxLevel: 4,
subMaxLevel: 2,
name: 'FlashDB',
logo: '_media/flashdb.png height="48px"',
nameLink: 'https://github.com/armink/FlashDB',
repo: 'armink/FlashDB',
coverpage: ['/', '/zh-cn/'],
requestHeaders: {
'cache-control': 'max-age=3600',
},
formatUpdated: '{YYYY}-{MM}-{DD} {HH}:{mm}',
search: {
noData: {
'/zh-cn/': '没有结果!',
'/': 'No results!'
},
paths: 'auto',
placeholder: {
'/zh-cn/': '搜索',
'/': 'Search'
}
},
plugins: [
function (hook, vm) {
hook.beforeEach(function (html) {
var url = 'https://github.com/armink/FlashDB/blob/master/docs/' + vm.route.file
var editHtml = '[:memo: Edit this page](' + url + ')\n'
return '_Last modified {docsify-updated}_ ' +
editHtml +
html +
'\n----\n' +
'>Powered by [FlashDB](https://github.com/armink/FlashDB)'
})
}
]
}
</script>
<script src="_scripts/docsify.min.js"></script>
<script src="_scripts/search.min.js"></script>
<script src="_scripts/emoji.min.js"></script>
<script src="_scripts/prism-c.min.js"></script>
</body>
</html>

143
docs/porting.md Normal file
View File

@ -0,0 +1,143 @@
# Porting Guide
The underlying Flash management and operation of FlashDB relies on RT-Thread's FAL (Flash Abstraction Layer) Flash abstraction layer open source software package. This open source library also supports running on **bare metal platform** [(click to view introduction)](http:/ /packages.rt-thread.org/detail.html?package=fal). So only need to connect the used Flash to FAL to complete the whole transplantation work.
## Introduction to Porting
![flashdb_porting_layer](_media/flashdb_porting_layer.png)
The main work of transplantation is on the FAL side. Other interfaces are not strongly dependent, and can be connected according to your own situation.
It is recommended to understand the FAL function introduction before transplantation, see: http://packages.rt-thread.org/detail.html?package=fal
The bottom layer of FAL encapsulates different Flash storage media in a unified manner and provides a partition table mechanism to expose it to upper users.
Each database of FlashDB is based on the partitioning mechanism provided by FAL. Each database is located on a FAL partition, which is equivalent to a partition corresponding to a database.
The following will explain the FAL porting process in detail. For more porting demonstrations, please refer to the demo provided.
## FAL migration
### Define flash device
Before defining the Flash device table, you need to define the Flash device first. It can be on-chip flash, or off-chip SFUD-based spi flash:
- Refer to [`fal_flash_stm32f2_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_stm32f2_port.c) to define the on-chip flash device.
- Refer to [`fal_flash_sfud_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_sfud_port.c) to define off-chip spi flash device.
To define specific Flash device objects, users need to implement the operation functions of ʻinit`, `read`, `write`, ʻerase` according to their own Flash conditions:
- `static int init(void)`: **Optional** initialization operation.
- `static int read(long offset, uint8_t *buf, size_t size)`: read operation.
| Parameters | Description |
| ------ | ------------------------- |
| offset | Flash offset address for reading data |
| buf | Buffer to store the data to be read |
| size | The size of the data to be read |
| return | Return the actual read data size |
- `static int write(long offset, const uint8_t *buf, size_t size)`: write operation.
| Parameters | Description |
| ------ | ------------------------- |
| offset | Flash offset address for writing data |
| buf | Buffer for storing data to be written |
| size | The size of the data to be written |
| return | Return the actual written data size |
- `static int erase(long offset, size_t size)`: erase operation.
| Parameters | Description |
| ------ | ------------------------- |
| offset | Flash offset address of erase area |
| size | The size of the erased area |
| return | Return the actual erased area size |
Users need to implement these operation functions according to their own Flash conditions. A specific Flash device object is defined at the bottom of the file. The following example defines stm32f2 on-chip flash: stm32f2_onchip_flash
```C
const struct fal_flash_dev stm32f2_onchip_flash =
{
.name = "stm32_onchip",
.addr = 0x08000000,
.len = 1024*1024,
.blk_size = 128*1024,
.ops = {init, read, write, erase},
.write_gran = 8
};
```
- `"stm32_onchip"`: the name of the flash device.
- `0x08000000`: Start address for flash operation.
- `1024*1024`: Total size of Flash (1MB).
- `128*1024`: Flash block/sector size (because the STM32F2 blocks have uneven sizes, the erase granularity is the largest block size: 128K).
- `{init, read, write, erase}`: Flash operation functions. If there is no init initialization process, the first operation function position can be left blank.
- `8`: Set the write granularity, the unit is bit, 0 means not effective (default value is 0), this member is a new member whose fal version is greater than 0.4.0. Each flash write granularity is not the same, it can be set by this member, the following are several common Flash write granularities:
- nor flash: 1 bit
- stm32f2/f4: 8 bit
- stm32f1: 32 bit
- stm32l4: 64 bit
### Define the flash device table
The Flash device table is defined in the header file of `fal_cfg.h`, you need to **create a new `fal_cfg.h` file** before defining the partition table. Please place this file in the port folder of the corresponding BSP or project directory, and Add the header file path to the project. fal_cfg.h can be completed by referring to [Sample file fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h).
Equipment table example:
```C
/* ===================== Flash device Configuration ========================= */
extern const struct fal_flash_dev stm32f2_onchip_flash;
extern struct fal_flash_dev nor_flash0;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&stm32f2_onchip_flash, \
&nor_flash0, \
}
```
In the Flash device table, there are two Flash objects, one is the on-chip Flash of STM32F2, and the other is the off-chip Nor Flash.
### Define flash partition table
The partition table is also defined in the `fal_cfg.h` header file. Flash partitions are based on Flash devices, and each Flash device can have N partitions. The collection of these partitions is the partition table. Before configuring the partition table, make sure that the **Flash device** and **device table** have been defined. fal_cfg.h can be completed by referring to [Sample file fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h).
Partition table example:
```C
#define NOR_FLASH_DEV_NAME "norflash0"
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \
{FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \
{FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
```
The detailed description of the above partition table is as follows:
| Partition name | Flash device name | Offset address | Size | Description |
| ----------- | -------------- | --------- | ----- | ------ ------------ |
| "bl" | "stm32_onchip" | 0 | 64KB | Bootloader |
| "app" | "stm32_onchip" | 64*1024 | 704KB | Application |
| "easyflash" | "norflash0" | 0 | 1MB | EasyFlash parameter storage |
| "download" | "norflash0" | 1024*1024 | 1MB | OTA download area |
The partition parameters that users need to modify include: partition name, associated Flash device name, offset address (relative to the internal Flash device), and size. Pay attention to the following points:
- Partition name guarantee **cannot be repeated**;
- The associated Flash device **must have been defined in the Flash device table**, and the **name is the same**, otherwise there will be an error that the Flash device cannot be found;
- The starting address and size of the partition **cannot exceed the address range of the Flash device**, otherwise it will cause packet initialization errors;
> Note: When defining each partition, in addition to filling in the parameter attributes described above, you need to add the attribute `FAL_PART_MAGIC_WORD` at the front and add `0` at the end (currently used for reserved functions)

61
docs/quick-started.md Normal file
View File

@ -0,0 +1,61 @@
# Quick start
This document will help users quickly use FlashDB on the demo platform and experience the actual use of FlashDB
## basic concepts
- **Key-Value Database (KVDB)**: It is a non-relational database that stores data as a collection of key-value pairs, where the key is used as a unique identifier. KVDB has simple operation and strong scalability.
- **Time Series Data (TSDB)**: Time Series Database (TSDB), which stores data in **time sequence**. TSDB data has a timestamp, a large amount of data storage, and high insertion and query performance.
- **Time series log (TSL)**: TSL (Time series log) is the abbreviation of each record in TSDB.
- **Blob**: In a computer, blob is often a field type used to store binary files in a database. In FlashDB, both KV and TSL use the blob type for storage, which can be compatible with any variable type.
- **Iterator**: It allows users to visit every element in the container through a specific interface without knowing the underlying implementation. Both TSDB and KVDB support traversal access to the database through iterators.
## Functional block diagram
Through the following functional block diagram, you can roughly understand the FlashDB functional module division
![flashdb_framework](_media/flashdb_framework.png)
## Prepare the development environment
Before use, you need to install the following development software on your PC in advance
### Integrated Development Environment
The demo project provided by FlashDB supports two projects by default:
- **RT-Studio**: Free to use, easy to use, download address: https://www.rt-thread.io/studio.html
- **Keil MDK**: MDK v5 version of the integrated development environment needs to be installed
### Serial port tool
Prepare `Serial debugging assistant` or `Serial terminal tool`, and you need to use it when viewing the running log later
## Get the source code
The latest code is currently hosted on GitHub. The master branch is the development version. It is recommended to download the released version
- GitHub download: https://github.com/armink/FlashDB/releases
## Choose a demo platform
In the `demos` directory of the project, the following hardware demonstration platforms are currently provided. You can choose a hardware platform and experience the running process of FlashDB on a real machine.
For more detailed introduction, click on the **instructions** in the table below to view.
| Hardware Platform | Path | Flash Type | Instructions |
| --------------------- | ----------------------------- | :------------ | ---------------------------------------------------- |
| stm32f10x | `demos/stm32f103ve` | stm32 on-chip | [click to view](demo-stm32f103ve.md) |
| stm32f40x | `demos/stm32f405rg` | stm32 on-chip | [click to view](demo-stm32f405rg.md) |
| stm32f40x + spi flash | `demos/stm32f405rg_spi_flash` | spi flash | [click to view](demo-stm32f405rg-spi-flash.md) |
## View sample description
If you don't have a suitable demo platform above, you can also check the example instructions you are interested in first.
| Sample file | Description | Detailed explanation |
| ----------------------------------- | ------------------------- | ---------------------------------------------- |
| `samples/kvdb_basic_sample.c` | KVDB basic example | [click to view](sample-kvdb-basic) |
| `samples/kvdb_type_string_sample.c` | KV example of string type | [click to view](sample-kvdb-type-string) |
| `samples/kvdb_type_blob_sample.c` | Blob type KV example | [click to view](sample-kvdb-type-blob) |
| `samples/tsdb_sample.c` | TSDB basic example | [click to view](sample-tsdb-basic) |

View File

@ -1,4 +1,73 @@
|File or folder name |Description|
|:----- |:----|
|en |English documents|
|zh |中文文档(简体)|
# FlashDBAn ultra-lightweight embedded database
[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB) [![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE)
## Introduction
[FlashDB](https://github.com/armink/FlashDB) is an ultra-lightweight embedded database that focuses on providing data storage solutions for embedded products. Different from traditional database based on file system, [FlashDB](https://github.com/armink/FlashDB) combines the features of Flash and has strong performance and reliability. And under the premise of ensuring extremely low resource occupation, the service life of Flash should be extended as much as possible.
[FlashDB](https://github.com/armink/FlashDB) provides two database modes:
- **Key-value database**: It is a non-relational database that stores data as a collection of key-value pairs, where the key is used as a unique identifier. KVDB has simple operation and strong scalability.
- **Time Series Database**: Time Series Database (TSDB), which stores data in **time sequence**. TSDB data has a timestamp, a large amount of data storage, and high insertion and query performance.
## Usage scenario
Nowadays, there are more and more types of IoT products, and the types and total amount of data generated during operation are also increasing. FlashDB provides a variety of data storage solutions, not only has a small resource footprint, but also has a large storage capacity, which is very suitable for IoT products. The following are the main application scenarios:
- **Key-value database**:
- Product parameter storage
- User configuration information storage
- Small file management
- **Time Series Database**:
- Store dynamically generated structured data: such as environmental monitoring information collected by temperature and humidity sensors, human health information recorded in real time by smart bracelets, etc.
- Record operation log: store operation log of product history, record of abnormal alarm, etc.
## Key Features
- Very small footprint, ram usage is almost **0**;
- Support multiple partitions, **multiple instances**. When the amount of data is large, the partition can be refined to reduce the retrieval time;
- Support **wear balance** to extend Flash life;
- Support **Power-off protection** function, high reliability;
- Supports two KV types, string and blob, which is convenient for users to operate;
- Support KV **incremental upgrade**, after product firmware upgrade, KVDB content also supports automatic upgrade;
- Support to modify the status of each TSDB record to facilitate user management;
## Performance and footprint
### TSDB performance test 1 (nor flash W25Q64)
```shell
msh />tsl bench
Append 1250 TSL in 5 seconds, average: 250.00 tsl/S, 4.00 ms/per
Query total spent 2218 (ms) for 1251 TSL, min 1, max 2, average: 1.77 ms/per
```
Insert average: 4 ms, query average: 1.8 ms
### TSDB performance test 2 (stm32f2 onchip flash)
```shell
msh />tsl bench
Append 13421 TSL in 5 seconds, average: 2684.20 tsl/S, 0.37 ms/per
Query total spent 1475 (ms) for 13422 TSL, min 0, max 1, average: 0.11 ms/per
```
Insert average: 0.37 ms, query average: 0.12 ms
### Footprint (stm32f4 IAR8.20)
```shell
Module ro code ro data rw data
------ ------- ------- -------
fdb.o 276 232 1
fdb_kvdb.o 4 584 356 1
fdb_tsdb.o 1 160 236
fdb_utils.o 418 1 024
```
The above is the map file information of IAR. It can be seen that the footprint of FlashDB is very small.
## License
The project uses the Apache-2.0 open source protocol. For details, please read the contents of the LICENSE file in the project.

60
docs/sample-kvdb-basic.md Normal file
View File

@ -0,0 +1,60 @@
# KVDB basic example
This example mainly demonstrates the basic functions of KVDB, including KV acquisition and setting modification functions.
## Code description
The sample code is located in `samples/kvdb_basic_sample.c`, the default KV table is defined in `main.c`, and there is `boot_count` KV in it. The KV is used to record the current system startup times. Each time the power is turned off and restarted, the KV will be automatically incremented by one and saved to KVDB. The general content is as follows:
```C
void kvdb_basic_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
int boot_count = 0;
FDB_INFO("==================== kvdb_basic_sample ====================\n");
{ /* GET the KV value */
/* get the "boot_count" KV value */
fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
} else {
FDB_INFO("get the 'boot_count' failed\n");
}
}
{ /* CHANGE the KV value */
/* increase the boot count */
boot_count ++;
/* change the "boot_count" KV's value */
fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
}
FDB_INFO("===========================================================\n");
}
```
## First run log
The current `boot_count` is 0, after adding one, it is stored in the database.
```
[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ====================
[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 0
[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 1
[FlashDB][sample][kvdb][basic] ===========================================================
```
## Secondary run log
The current `boot_count` is 1, indicating that the last save is effective, add one to it and save it for the next visit.
```
[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ====================
[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 1
[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 2
[FlashDB][sample][kvdb][basic] ===========================================================
```

View File

@ -0,0 +1,38 @@
# Traverse all KV
This example demonstrates that if you traverse all KVs in KVDB, users can add their own processing actions when traversing KVs.
## Code description
In the following sample code, first initialize the iterator of KVDB, and then use the iterator API to traverse all KVs of KVDB one by one.
The traversed KV object contains some attributes of KV, including: key name, value saved addr, value length, etc. The user can read it out through `fdb_blob_read` in cooperation with `fdb_kv_to_blob`, and do some business processing of his own.
```C
void kvdb_tarversal_sample(fdb_kvdb_t kvdb)
{
struct fdb_kv_iterator iterator;
fdb_kv_t cur_kv;
struct fdb_blob blob;
size_t data_size;
uint8_t *data_buf;
fdb_kv_iterator_init(&iterator);
while (fdb_kv_iterate(kvdb, &iterator)) {
cur_kv = &(iterator.curr_kv);
data_size = (size_t) cur_kv->value_len;
data_buf = (uint8_t *) malloc(data_size);
if (data_buf == NULL) {
FDB_INFO("Error: malloc failed.\n");
break;
}
fdb_blob_read((fdb_db_t) kvdb, fdb_kv_to_blob(cur_kv, fdb_blob_make(&blob, data_buf, data_size)));
/*
* balabala do what ever you like with blob...
*/
free(data_buf);
}
}
```

View File

@ -0,0 +1,71 @@
# blob type KV example
This example mainly demonstrates the related functions of blob KV. Blob KV is a more commonly used type, and its value is a binary type without type restrictions. Functionally, blob KV is also compatible with string KV. In the use of API, blob KV has a set of independent APIs, which can quickly realize the storage of various types of KV to KVDB, such as basic types, arrays and structures.
## Code description
The sample code is located in `samples/kvdb_type_blob.c`, and a KV named `"temp"` is used to store the temperature value, respectively demonstrating the whole process of blob KV from `create->read->modify->delete`. The general content is as follows:
```C
void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
FDB_INFO("==================== kvdb_type_blob_sample ====================\n");
{ /* CREATE new Key-Value */
int temp_data = 36;
/* It will create new KV node when "temp" KV not in database.
* fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
*/
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data);
}
{ /* GET the KV value */
int temp_data = 0;
/* get the "temp" KV value */
fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'temp' value is: %d\n", temp_data);
}
}
{ /* CHANGE the KV value */
int temp_data = 38;
/* change the "temp" KV's value to 38 */
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
FDB_INFO("set 'temp' value to %d\n", temp_data);
}
{ /* DELETE the KV by name */
fdb_kv_del(kvdb, "temp");
FDB_INFO("delete the 'temp' finish\n");
}
FDB_INFO("===========================================================\n");
}
```
## Running log
It can be seen from the log:
- First create a KV named `"temp"` and give the initial value 36℃
- Read the current value of `"temp"` KV and find that it is the same as the initial value
- Modify the value of `"temp"` KV to 38℃
- Finally delete `"temp"` KV
```
[FlashDB][sample][kvdb][blob] ==================== kvdb_type_blob_sample ====================
[FlashDB][sample][kvdb][blob] create the 'temp' blob KV, value is: 36
[FlashDB][sample][kvdb][blob] get the 'temp' value is: 36
[FlashDB][sample][kvdb][blob] set 'temp' value to 38
[FlashDB][sample][kvdb][blob] delete the 'temp' finish
[FlashDB][sample][kvdb][blob] ===========================================================
```

View File

@ -0,0 +1,69 @@
# String type KV example
This example mainly demonstrates the related functions of the string KV. As a special KV type, the Key and Value of the string KV are both strings, which are often used in scenarios with high readability requirements such as parameter storage and command storage.
## Code description
The sample code is located in `samples/kvdb_type_string.c`, and a KV named `"temp"` is used to store the temperature value, which respectively demonstrates the whole process of the string KV from `create->read->modify->delete` . The general content is as follows:
```C
void kvdb_type_string_sample(fdb_kvdb_t kvdb)
{
FDB_INFO("==================== kvdb_type_string_sample ====================\n");
{ /* CREATE new Key-Value */
char temp_data[10] = "36C";
/* It will create new KV node when "temp" KV not in database. */
fdb_kv_set(kvdb, "temp", temp_data);
FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data);
}
{ /* GET the KV value */
char *return_value, temp_data[10] = { 0 };
/* Get the "temp" KV value.
* NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
*/
return_value = fdb_kv_get(kvdb, "temp");
/* the return value is NULL when get the value failed */
if (return_value != NULL) {
strncpy(temp_data, return_value, sizeof(temp_data));
FDB_INFO("get the 'temp' value is: %s\n", temp_data);
}
}
{ /* CHANGE the KV value */
char temp_data[10] = "38C";
/* change the "temp" KV's value to "38.1" */
fdb_kv_set(kvdb, "temp", temp_data);
FDB_INFO("set 'temp' value to %s\n", temp_data);
}
{ /* DELETE the KV by name */
fdb_kv_del(kvdb, "temp");
FDB_INFO("delete the 'temp' finish\n");
}
FDB_INFO("===========================================================\n");
}
```
## Run log
It can be seen from the log:
- First create a KV named `"temp"` and give the initial value 36℃
- Read the current value of `"temp"` KV and find that it is the same as the initial value
- Modify the value of `"temp"` KV to 38℃
- Finally delete `"temp"` KV
```
[FlashDB][sample][kvdb][string] ==================== kvdb_type_string_sample ====================
[FlashDB][sample][kvdb][string] create the 'temp' string KV, value is: 36C
[FlashDB][sample][kvdb][string] get the 'temp' value is: 36C
[FlashDB][sample][kvdb][string] set 'temp' value to 38C
[FlashDB][sample][kvdb][string] delete the 'temp' finish
[FlashDB][sample][kvdb][string] ===========================================================
```

169
docs/sample-tsdb-basic.md Normal file
View File

@ -0,0 +1,169 @@
# TSDB basic example
This example mainly demonstrates the basic functions of TSDB, including TSL (time sequence record) addition, query and status modification functions.
## Code description
The sample code is located in `samples/tsdb_sample.c`, including the processes of appending, querying and status modification. The approximate code is as follows:
```C
void tsdb_sample(fdb_tsdb_t tsdb)
{
struct fdb_blob blob;
FDB_INFO("==================== tsdb_sample ====================\n");
{ /* APPEND new TSL (time series log) */
struct env_status status;
/* append new log to TSDB */
status.temp = 36;
status.humi = 85;
fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
status.temp = 38;
status.humi = 90;
fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
}
{ /* QUERY the TSDB */
/* query all TSL in TSDB by iterator */
fdb_tsl_iter(tsdb, query_cb, tsdb);
}
{ /* QUERY the TSDB by time */
/* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
size_t count;
/* query all TSL in TSDB by time */
fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb);
/* query all FDB_TSL_WRITE status TSL's count in TSDB by time */
count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE);
FDB_INFO("query count is: %u\n", count);
}
{ /* SET the TSL status */
/* Change the TSL status by iterator or time iterator
* set_status_cb: the change operation will in this callback
*
* NOTE: The actions to modify the state must be in orderC.
* like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
* The intermediate states can also be ignored.
* such as: FDB_TSL_WRITE -> FDB_TSL_DELETED
*/
fdb_tsl_iter(tsdb, set_status_cb, tsdb);
}
FDB_INFO("===========================================================\n");
}
```
Let's look at these processes separately
- **Append**: Modify the value of the structure object `status` twice and append it to TSDB;
- **Query**: Through TSDB's iterator API, the `query_cb` callback function will be automatically executed during each iteration to query all records in TSDB. The content of the callback function is as follows:
```C
static bool query_cb(fdb_tsl_t tsl, void *arg)
{
struct fdb_blob blob;
struct env_status status;
fdb_tsdb_t db = arg;
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
return false;
}
```
- **Query by time**: TSDB also provides an API for iterating by time: `fdb_tsl_iter_by_time`, you can pass in the start and end time, and the iterator will iterate the time series records according to the passed time period. In each iteration, the `query_by_time_cb` callback is executed, and the content of the current record is read in the callback and printed out.
```C
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg)
{
struct fdb_blob blob;
struct env_status status;
fdb_tsdb_t db = arg;
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
return false;
}
```
- **Modify Status**: After each TSL is added to TSDB, its status can be modified. There are 4 types of status:
- `FDB_TSL_WRITE`: written state, the default state after TSL is appended to TSDB;
- `FDB_TSL_USER_STATUS1`: The status is between writing and deleting. Users can customize the meaning of the status, such as: data has been synchronized to the cloud;
- `FDB_TSL_DELETED`: Deleted state, when TSL needs to be deleted, just modify the state of TSL to this state;
> Tip: In FlashDB, in order to improve the life of the Flash, the delete action does not actually erase the data from the Flash, but marks it as a deleted state. The user can distinguish different data records by state.
- `FDB_TSL_USER_STATUS2`: the customized status after the status is deleted, reserved for users;
When modifying the status, it can only be modified in the order of `FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2`, and cannot be modified in reverse order. It is also possible to skip intermediate states, for example: directly change from `FDB_TSL_WRITE` to `FDB_TSL_DELETED` state, and skip `FDB_TSL_USER_STATUS1` state.
In the example, all current TSLs are modified to the status of `FDB_TSL_USER_STATUS1` through the iterator. The callback code in the iterator is as follows:
```C
static bool set_status_cb(fdb_tsl_t tsl, void *arg)
{
fdb_tsdb_t db = arg;
FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1);
fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
return false;
}
```
## First run log
It can be seen from the log that the example first adds two TSLs, and each TSL stores different temperature and humidity records. Then obtain the TSL in TSDB through ordinary query and query by time, and finally modify its status from `2: FDB_TSL_WRITE` to `3: FDB_TSL_USER_STATUS1`.
```
[FlashDB][sample][tsdb] ==================== tsdb_sample ====================
[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85)
[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90)
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] query count is: 2
[FlashDB][sample][tsdb] set the TSL (time 1) status from 2 to 3
[FlashDB][sample][tsdb] set the TSL (time 2) status from 2 to 3
[FlashDB][sample][tsdb] ===========================================================
```
## Secondary run log
In the second run, two TSLs will still be added. Look at the results of the query, there are 4 in total, including 2 TSLs that were added at the first run. It can be seen from the printed time stamp that the analog time stamp works normally. `query count is: 2` shows that although TSDB has 4 records, only 2 records are in the write state.
```
[FlashDB][sample][tsdb] ==================== tsdb_sample ====================
[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85)
[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90)
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 3, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 4, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 3, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 4, temp: 38, humi: 90
[FlashDB][sample][tsdb] query count is: 2
[FlashDB][sample][tsdb] set the TSL (time 1) status from 3 to 3
[FlashDB][sample][tsdb] set the TSL (time 2) status from 3 to 3
[FlashDB][sample][tsdb] set the TSL (time 3) status from 2 to 3
[FlashDB][sample][tsdb] set the TSL (time 4) status from 2 to 3
[FlashDB][sample][tsdb] ===========================================================
```

829
docs/vue.css Normal file
View File

@ -0,0 +1,829 @@
@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");
* {
-webkit-font-smoothing: antialiased;
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-text-size-adjust: none;
-webkit-touch-callout: none;
box-sizing: border-box;
}
body:not(.ready) {
overflow: hidden;
}
body:not(.ready) [data-cloak],
body:not(.ready) .app-nav,
body:not(.ready) > nav {
display: none;
}
div#app {
font-size: 30px;
font-weight: lighter;
margin: 40vh auto;
text-align: center;
}
div#app:empty::before {
content: 'Loading...';
}
.emoji {
height: 1.2rem;
vertical-align: middle;
}
.progress {
background-color: var(--theme-color, #42b983);
height: 2px;
left: 0px;
position: fixed;
right: 0px;
top: 0px;
transition: width 0.2s, opacity 0.4s;
width: 0%;
z-index: 999999;
}
.search a:hover {
color: var(--theme-color, #42b983);
}
.search .search-keyword {
color: var(--theme-color, #42b983);
font-style: normal;
font-weight: bold;
}
html,
body {
height: 100%;
}
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
color: #34495e;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 15px;
letter-spacing: 0;
margin: 0;
overflow-x: hidden;
}
img {
max-width: 100%;
}
a[disabled] {
cursor: not-allowed;
opacity: 0.6;
}
kbd {
border: solid 1px #ccc;
border-radius: 3px;
display: inline-block;
font-size: 12px !important;
line-height: 12px;
margin-bottom: 3px;
padding: 3px 5px;
vertical-align: middle;
}
li input[type='checkbox'] {
margin: 0 0.2em 0.25em 0;
vertical-align: middle;
}
.app-nav {
margin: 25px 60px 0 0;
position: absolute;
right: 0;
text-align: right;
z-index: 10;
/* navbar dropdown */
}
.app-nav.no-badge {
margin-right: 25px;
}
.app-nav p {
margin: 0;
}
.app-nav > a {
margin: 0 1rem;
padding: 5px 0;
}
.app-nav ul,
.app-nav li {
display: inline-block;
list-style: none;
margin: 0;
}
.app-nav a {
color: inherit;
font-size: 16px;
text-decoration: none;
transition: color 0.3s;
}
.app-nav a:hover {
color: var(--theme-color, #42b983);
}
.app-nav a.active {
border-bottom: 2px solid var(--theme-color, #42b983);
color: var(--theme-color, #42b983);
}
.app-nav li {
display: inline-block;
margin: 0 1rem;
padding: 5px 0;
position: relative;
cursor: pointer;
}
.app-nav li ul {
background-color: #fff;
border: 1px solid #ddd;
border-bottom-color: #ccc;
border-radius: 4px;
box-sizing: border-box;
display: none;
max-height: calc(100vh - 61px);
overflow-y: auto;
padding: 10px 0;
position: absolute;
right: -15px;
text-align: left;
top: 100%;
white-space: nowrap;
}
.app-nav li ul li {
display: block;
font-size: 14px;
line-height: 1rem;
margin: 0;
margin: 8px 14px;
white-space: nowrap;
}
.app-nav li ul a {
display: block;
font-size: inherit;
margin: 0;
padding: 0;
}
.app-nav li ul a.active {
border-bottom: 0;
}
.app-nav li:hover ul {
display: block;
}
.github-corner {
border-bottom: 0;
position: fixed;
right: 0;
text-decoration: none;
top: 0;
z-index: 1;
}
.github-corner:hover .octo-arm {
-webkit-animation: octocat-wave 560ms ease-in-out;
animation: octocat-wave 560ms ease-in-out;
}
.github-corner svg {
color: #fff;
fill: var(--theme-color, #42b983);
height: 80px;
width: 80px;
}
main {
display: block;
position: relative;
width: 100vw;
height: 100%;
z-index: 0;
}
main.hidden {
display: none;
}
.anchor {
display: inline-block;
text-decoration: none;
transition: all 0.3s;
}
.anchor span {
color: #34495e;
}
.anchor:hover {
text-decoration: underline;
}
.sidebar {
border-right: 1px solid rgba(0,0,0,0.07);
overflow-y: auto;
padding: 40px 0 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
transition: transform 250ms ease-out;
width: 300px;
z-index: 20;
}
.sidebar > h1 {
margin: 0 auto 1rem;
font-size: 1.5rem;
font-weight: 300;
text-align: center;
}
.sidebar > h1 a {
color: inherit;
text-decoration: none;
}
.sidebar > h1 .app-nav {
display: block;
position: static;
}
.sidebar .sidebar-nav {
line-height: 2em;
padding-bottom: 40px;
}
.sidebar li.collapse .app-sub-sidebar {
display: none;
}
.sidebar ul {
margin: 0 0 0 15px;
padding: 0;
}
.sidebar li > p {
font-weight: 700;
margin: 0;
}
.sidebar ul,
.sidebar ul li {
list-style: none;
}
.sidebar ul li a {
border-bottom: none;
display: block;
}
.sidebar ul li ul {
padding-left: 20px;
}
.sidebar::-webkit-scrollbar {
width: 4px;
}
.sidebar::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 4px;
}
.sidebar:hover::-webkit-scrollbar-thumb {
background: rgba(136,136,136,0.4);
}
.sidebar:hover::-webkit-scrollbar-track {
background: rgba(136,136,136,0.1);
}
.sidebar-toggle {
background-color: transparent;
background-color: rgba(255,255,255,0.8);
border: 0;
outline: none;
padding: 10px;
position: absolute;
bottom: 0;
left: 0;
text-align: center;
transition: opacity 0.3s;
width: 284px;
z-index: 30;
cursor: pointer;
}
.sidebar-toggle:hover .sidebar-toggle-button {
opacity: 0.4;
}
.sidebar-toggle span {
background-color: var(--theme-color, #42b983);
display: block;
margin-bottom: 4px;
width: 16px;
height: 2px;
}
body.sticky .sidebar,
body.sticky .sidebar-toggle {
position: fixed;
}
.content {
padding-top: 60px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 300px;
transition: left 250ms ease;
}
.markdown-section {
margin: 0 auto;
max-width: 80%;
padding: 30px 15px 40px 15px;
position: relative;
}
.markdown-section > * {
box-sizing: border-box;
font-size: inherit;
}
.markdown-section > :first-child {
margin-top: 0 !important;
}
.markdown-section hr {
border: none;
border-bottom: 1px solid #eee;
margin: 2em 0;
}
.markdown-section iframe {
border: 1px solid #eee;
/* fix horizontal overflow on iOS Safari */
width: 1px;
min-width: 100%;
}
.markdown-section table {
border-collapse: collapse;
border-spacing: 0;
display: block;
margin-bottom: 1rem;
overflow: auto;
width: 100%;
}
.markdown-section th {
border: 1px solid #ddd;
font-weight: bold;
padding: 6px 13px;
}
.markdown-section td {
border: 1px solid #ddd;
padding: 6px 13px;
}
.markdown-section tr {
border-top: 1px solid #ccc;
}
.markdown-section tr:nth-child(2n) {
background-color: #f8f8f8;
}
.markdown-section p.tip {
background-color: #f8f8f8;
border-bottom-right-radius: 2px;
border-left: 4px solid #f66;
border-top-right-radius: 2px;
margin: 2em 0;
padding: 12px 24px 12px 30px;
position: relative;
}
.markdown-section p.tip:before {
background-color: #f66;
border-radius: 100%;
color: #fff;
content: '!';
font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 14px;
font-weight: bold;
left: -12px;
line-height: 20px;
position: absolute;
height: 20px;
width: 20px;
text-align: center;
top: 14px;
}
.markdown-section p.tip code {
background-color: #efefef;
}
.markdown-section p.tip em {
color: #34495e;
}
.markdown-section p.warn {
background: rgba(66,185,131,0.1);
border-radius: 2px;
padding: 1rem;
}
.markdown-section ul.task-list > li {
list-style-type: none;
}
body.close .sidebar {
transform: translateX(-300px);
}
body.close .sidebar-toggle {
width: auto;
}
body.close .content {
left: 0;
}
@media print {
.github-corner,
.sidebar-toggle,
.sidebar,
.app-nav {
display: none;
}
}
@media screen and (max-width: 768px) {
.github-corner,
.sidebar-toggle,
.sidebar {
position: fixed;
}
.app-nav {
margin-top: 16px;
}
.app-nav li ul {
top: 30px;
}
main {
height: auto;
overflow-x: hidden;
}
.sidebar {
left: -300px;
transition: transform 250ms ease-out;
}
.content {
left: 0;
max-width: 100vw;
position: static;
padding-top: 20px;
transition: transform 250ms ease;
}
.app-nav,
.github-corner {
transition: transform 250ms ease-out;
}
.sidebar-toggle {
background-color: transparent;
width: auto;
padding: 30px 30px 10px 10px;
}
body.close .sidebar {
transform: translateX(300px);
}
body.close .sidebar-toggle {
background-color: rgba(255,255,255,0.8);
transition: 1s background-color;
width: 284px;
padding: 10px;
}
body.close .content {
transform: translateX(300px);
}
body.close .app-nav,
body.close .github-corner {
display: none;
}
.github-corner:hover .octo-arm {
-webkit-animation: none;
animation: none;
}
.github-corner .octo-arm {
-webkit-animation: octocat-wave 560ms ease-in-out;
animation: octocat-wave 560ms ease-in-out;
}
}
@-webkit-keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-25deg);
}
40%, 80% {
transform: rotate(10deg);
}
}
@keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-25deg);
}
40%, 80% {
transform: rotate(10deg);
}
}
section.cover {
align-items: center;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
height: 100vh;
width: 100vw;
display: none;
}
section.cover.show {
display: flex;
}
section.cover.has-mask .mask {
background-color: #fff;
opacity: 0.8;
position: absolute;
top: 0;
height: 100%;
width: 100%;
}
section.cover .cover-main {
flex: 1;
margin: -20px 16px 0;
text-align: center;
position: relative;
}
section.cover a {
color: inherit;
text-decoration: none;
}
section.cover a:hover {
text-decoration: none;
}
section.cover p {
line-height: 1.5rem;
margin: 1em 0;
}
section.cover h1 {
color: inherit;
font-size: 2.5rem;
font-weight: 300;
margin: 0.625rem 0 2.5rem;
position: relative;
text-align: center;
}
section.cover h1 a {
display: block;
}
section.cover h1 small {
bottom: -0.4375rem;
font-size: 1rem;
position: absolute;
}
section.cover blockquote {
font-size: 1.5rem;
text-align: center;
}
section.cover ul {
line-height: 1.8;
list-style-type: none;
margin: 1em auto;
max-width: 500px;
padding: 0;
}
section.cover .cover-main > p:last-child a {
border-color: var(--theme-color, #42b983);
border-radius: 2rem;
border-style: solid;
border-width: 1px;
box-sizing: border-box;
color: var(--theme-color, #42b983);
display: inline-block;
font-size: 1.05rem;
letter-spacing: 0.1rem;
margin: 0.5rem 1rem;
padding: 0.75em 2rem;
text-decoration: none;
transition: all 0.15s ease;
}
section.cover .cover-main > p:last-child a:last-child {
background-color: var(--theme-color, #42b983);
color: #fff;
}
section.cover .cover-main > p:last-child a:last-child:hover {
color: inherit;
opacity: 0.8;
}
section.cover .cover-main > p:last-child a:hover {
color: inherit;
}
section.cover blockquote > p > a {
border-bottom: 2px solid var(--theme-color, #42b983);
transition: color 0.3s;
}
section.cover blockquote > p > a:hover {
color: var(--theme-color, #42b983);
}
body {
background-color: #fff;
}
/* sidebar */
.sidebar {
background-color: #fff;
color: #364149;
}
.sidebar li {
margin: 6px 0 6px 0;
}
.sidebar ul li a {
color: #505d6b;
font-size: 14px;
font-weight: normal;
overflow: hidden;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
}
.sidebar ul li a:hover {
text-decoration: underline;
}
.sidebar ul li ul {
padding: 0;
}
.sidebar ul li.active > a {
border-right: 2px solid;
color: var(--theme-color, #42b983);
font-weight: 600;
}
.app-sub-sidebar li::before {
content: '-';
padding-right: 4px;
float: left;
}
/* markdown content found on pages */
.markdown-section h1,
.markdown-section h2,
.markdown-section h3,
.markdown-section h4,
.markdown-section strong {
color: #2c3e50;
font-weight: 600;
}
.markdown-section a {
color: var(--theme-color, #42b983);
font-weight: 600;
}
.markdown-section h1 {
font-size: 2rem;
margin: 0 0 1rem;
}
.markdown-section h2 {
font-size: 1.75rem;
margin: 45px 0 0.8rem;
}
.markdown-section h3 {
font-size: 1.5rem;
margin: 40px 0 0.6rem;
}
.markdown-section h4 {
font-size: 1.25rem;
}
.markdown-section h5 {
font-size: 1rem;
}
.markdown-section h6 {
color: #777;
font-size: 1rem;
}
.markdown-section figure,
.markdown-section p {
margin: 1.2em 0;
}
.markdown-section p,
.markdown-section ul,
.markdown-section ol {
line-height: 1.6rem;
word-spacing: 0.05rem;
}
.markdown-section ul,
.markdown-section ol {
padding-left: 1.5rem;
}
.markdown-section blockquote {
border-left: 4px solid var(--theme-color, #42b983);
color: #858585;
margin: 2em 0;
padding-left: 20px;
}
.markdown-section blockquote p {
font-weight: 600;
margin-left: 0;
}
.markdown-section iframe {
margin: 1em 0;
}
.markdown-section em {
color: #7f8c8d;
}
.markdown-section code {
background-color: #f8f8f8;
border-radius: 2px;
color: #e96900;
font-family: 'Roboto Mono', Monaco, courier, monospace;
font-size: 0.8rem;
margin: 0 2px;
padding: 3px 5px;
white-space: pre-wrap;
}
.markdown-section pre {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
background-color: #f8f8f8;
font-family: 'Roboto Mono', Monaco, courier, monospace;
line-height: 1.5rem;
margin: 1.2em 0;
overflow: auto;
padding: 0 1.4rem;
position: relative;
word-wrap: normal;
}
/* code highlight */
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8e908c;
}
.token.namespace {
opacity: 0.7;
}
.token.boolean,
.token.number {
color: #c76b29;
}
.token.punctuation {
color: #525252;
}
.token.property {
color: #c08b30;
}
.token.tag {
color: #2973b7;
}
.token.string {
color: var(--theme-color, #42b983);
}
.token.selector {
color: #6679cc;
}
.token.attr-name {
color: #2973b7;
}
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #22a2c9;
}
.token.attr-value,
.token.control,
.token.directive,
.token.unit {
color: var(--theme-color, #42b983);
}
.token.keyword,
.token.function {
color: #e96900;
}
.token.statement,
.token.regex,
.token.atrule {
color: #22a2c9;
}
.token.placeholder,
.token.variable {
color: #3d8fd1;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #202746;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.important {
color: #c94922;
}
.token.entity {
cursor: help;
}
.markdown-section pre > code {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
background-color: #f8f8f8;
border-radius: 2px;
color: #525252;
display: block;
font-family: 'Roboto Mono', Monaco, courier, monospace;
font-size: 0.8rem;
line-height: inherit;
margin: 0 2px;
max-width: inherit;
overflow: inherit;
padding: 2.2em 5px;
white-space: inherit;
}
.markdown-section code::after,
.markdown-section code::before {
letter-spacing: 0.05rem;
}
code .token {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
min-height: 1.5rem;
position: relative;
left: auto;
}
pre::after {
color: #ccc;
content: attr(data-lang);
font-size: 0.6rem;
font-weight: 600;
height: 15px;
line-height: 15px;
padding: 5px 10px 0;
position: absolute;
right: 0;
text-align: right;
top: 0;
}

79
docs/zh-cn/README.md Normal file
View File

@ -0,0 +1,79 @@
# FlashDB超轻量级嵌入式数据库
[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB) [![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE)
## 简介
[FlashDB](https://github.com/armink/FlashDB) 是一款超轻量级的嵌入式数据库,专注于提供嵌入式产品的数据存储方案。与传统的基于文件系统的数据库不同,[FlashDB](https://github.com/armink/FlashDB) 结合了 Flash 的特性,具有较强的性能及可靠性。并在保证极低的资源占用前提下,尽可能延长 Flash 使用寿命。
[FlashDB](https://github.com/armink/FlashDB) 提供两种数据库模式:
- **键值数据库** 是一种非关系数据库它将数据存储为键值Key-Value对集合其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。
- **时序数据库** :时间序列数据库 Time Series Database , 简称 TSDB它将数据按照 **时间顺序存储** 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。
## 使用场景
如今物联网产品种类越来越多运行时产生的数据种类及总量及也在不断变大。FlashDB 提供了多样化的数据存储方案,不仅资源占用小,并且存储容量大,非常适合用于物联网产品。下面是主要应用场景:
- **键值数据库**
- 产品参数存储
- 用户配置信息存储
- 小文件管理
- **时序数据库**
- 存储动态产生的结构化数据:如 温湿度传感器采集的环境监测信息,智能手环实时记录的人体健康信息等
- 记录运行日志:存储产品历史的运行日志,异常告警的记录等
## 主要特性
- 资源占用极低,内存占用几乎为 **0** ;
- 支持 多分区,**多实例** 。数据量大时,可细化分区,降低检索时间;
- 支持 **磨损平衡** ,延长 Flash 寿命;
- 支持 **掉电保护** 功能,可靠性高;
- 支持 字符串及 blob 两种 KV 类型,方便用户操作;
- 支持 KV **增量升级** ,产品固件升级后, KVDB 内容也支持自动升级;
- 支持 修改每条 TSDB 记录的状态,方便用户进行管理;
## 性能及资源占用
### TSDB 性能测试1 nor flash W25Q64
```shell
msh />tsl bench
Append 1250 TSL in 5 seconds, average: 250.00 tsl/S, 4.00 ms/per
Query total spent 2218 (ms) for 1251 TSL, min 1, max 2, average: 1.77 ms/per
```
插入平均4 ms查询平均1.8 ms
### TSDB 性能测试2 stm32f2 onchip flash
```shell
msh />tsl bench
Append 13421 TSL in 5 seconds, average: 2684.20 tsl/S, 0.37 ms/per
Query total spent 1475 (ms) for 13422 TSL, min 0, max 1, average: 0.11 ms/per
```
插入平均0.37 ms查询平均0.12 ms
### 资源占用 (stm32f4 IAR8.20)
```shell
Module ro code ro data rw data
------ ------- ------- -------
fdb.o 276 232 1
fdb_kvdb.o 4 584 356 1
fdb_tsdb.o 1 160 236
fdb_utils.o 418 1 024
```
上面是 IAR 的 map 文件信息,可见 FlashDB 的资源占用非常低
## 支持
![support](_media/wechat_support.png)
如果 FlashDB 解决了你的问题,不妨扫描上面二维码请我 **喝杯咖啡**~
## 许可
采用 Apache-2.0 开源协议,细节请阅读项目中的 LICENSE 文件内容。

15
docs/zh-cn/_coverpage.md Normal file
View File

@ -0,0 +1,15 @@
![FlashDB](../_media/flashdb.png)
> 一款超轻量级的嵌入式数据库
[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB)
[![version](https://img.shields.io/github/release/armink/FlashDB.svg?style=flat-square)](https://github/license/armink/FlashDB/releases/latest)
[![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE)
- 极低的资源占用
- 飞快的检索速度
- 键值数据库
- 时序数据库
[GitHub](https://github.com/armink/FlashDB/)
[开始使用](zh-cn/quick-started)

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

3
docs/zh-cn/_navbar.md Normal file
View File

@ -0,0 +1,3 @@
- Translations
- [:cn: 中文](/zh-cn/)
- [:uk: English](/)

19
docs/zh-cn/_sidebar.md Normal file
View File

@ -0,0 +1,19 @@
- 入门
- [快速开始](zh-cn/quick-started.md)
- 演示平台
- [stm32f103ve](zh-cn/demo-stm32f103ve.md)
- [stm32f405rg](zh-cn/demo-stm32f405rg.md)
- [stm32f405rg-spi-flash](zh-cn/demo-stm32f405rg-spi-flash.md)
- [演示说明](zh-cn/demo-details.md)
- 示例
- 键值数据库
- [基础](zh-cn/sample-kvdb-basic.md)
- [字符串类型](zh-cn/sample-kvdb-type-string.md)
- [blob 类型](zh-cn/sample-kvdb-type-blob.md)
- [遍历 KV](zh-cn/sample-kvdb-traversal.md)
- 时序数据库
- [基础](zh-cn/sample-tsdb-basic.md)
- 开发
- [移植](zh-cn/porting.md)
- [配置](zh-cn/configuration.md)
- [API](zh-cn/api.md)

347
docs/zh-cn/api.md Normal file
View File

@ -0,0 +1,347 @@
# API 说明
## Blob
### 构造 blob 对象
构造 blob 对象的过程,其内部是对 blob 结构体初始化赋值的过程,将传入的参数写入 blob 结构体中,并进行返回
`fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)`
| 参数 | 描述 |
| --------- | ---------------------- |
| blob | blob 初始对象 |
| value_buf | 存放数据的缓冲区 |
| buf_len | 缓冲区的大小 |
| 返回 | 创建完成后的 blob 对象 |
### 读取 blob 数据
通过 KVDB 和 TSDB 的 API 可以返回 blob 对象,此时返回的 blob 对象里存放了 blob 数据的存储地址。该 API 可以将数据库里存放的 blob 数据读取出来,并存放至 `blob->buf`
`size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)`
| 参数 | 描述 |
| ---- | -------------------------- |
| db | 数据库对象 |
| blob | blob 对象 |
| 返回 | 实际读取到的 blob 数据长度 |
## KVDB
### 初始化 KVDB
`fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv, void *user_data)`
| 参数 | 描述 |
| ---------- | -------------------------------------------------------- |
| db | 数据库对象 |
| name | 数据库名称 |
| part_name | 使用 FAL 分区表中的哪一个分区 |
| default_kv | 默认 KV 集合,第一次初始化时,将会把默认 KV 写入数据库中 |
| user_data | 用户自定义数据,没有时传入 NULL |
| 返回 | 错误码 |
### 控制 KVDB
通过命令控制字,用户可以对数据库进行一些控制操作
`void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)`
| 参数 | 描述 |
| ---- | ---------- |
| db | 数据库对象 |
| cmd | 命令控制字 |
| arg | 控制的参数 |
| 返回 | 错误码 |
支持的命令控制字如下:
```C
#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x0 /**< 设置扇区大小 */
#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x1 /**< 获取扇区大小 */
#define FDB_KVDB_CTRL_SET_LOCK 0x2 /**< 设置加锁函数 */
#define FDB_KVDB_CTRL_SET_UNLOCK 0x3 /**< 设置解锁函数 */
```
#### 扇区大小与块大小
FlashDB 内部存储结构由 N 个扇区组成,每次格式化时是以扇区作为最小单位。而一个扇区通常是 Flash 块大小的 N 倍,比如: Nor Flash 的块大小一般为 4096。
默认 KVDB 会使用 1倍 的块大小作为扇区大小4096。此时该 KVDB 无法存入超过 4096 长度的 KV 。如果想要存入比如10K 长度的 KV ,可以通过 control 函数,设置扇区大小为 12K或者更大大小即可。
### 设置 KV
使用此方法可以实现对 KV 的增加和修改功能。
- **增加** :当 KVDB 中不存在该名称的 KV 时,则会执行新增操作;
- **修改** :入参中的 KV 名称在当前 KVDB 中存在,则把该 KV 值修改为入参中的值;
通过 KV 的名字来获取其对应的值。支持两种接口
#### 设置 blob 类型KV
`fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)`
| 参数 | 描述 |
| ---- | ---------------------------- |
| db | 数据库对象 |
| key | KV 的名称 |
| blob | blob 对象,做为 KV 的 value |
| 返回 | 错误码 |
示例:
```C
struct fdb_blob blob;
int temp_data = 36;
/* 通过 fdb_blob_make 构造 blob 对象,作为 "temp" KV 的 value */
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
```
#### 设置 string 类型KV
`fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value)`
| 参数 | 描述 |
| ----- | ----------- |
| db | 数据库对象 |
| key | KV 的名称 |
| value | KV 的 value |
| 返回 | 错误码 |
### 获取 KV
#### 获取 blob 类型 KV
`size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)`
| 参数 | 描述 |
| ---- | ------------------------------------- |
| db | 数据库对象 |
| key | KV 的名称 |
| blob | 通过 blob 对象,返回 KV 的 blob value |
| 返回 | 错误码 |
示例:
```C
struct fdb_blob blob;
int temp_data = 0;
/* 构造 blob 对象,用于存储 get 回来的 "temp" KV 的 value 数据 */
fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
/* 如果有需要,可以检查 blob.saved.len 是否大于 0 ,确保 get 成功 */
if (blob.saved.len > 0) {
FDB_INFO("get the 'temp' value is: %d\n", temp_data);
}
```
#### 获取 KV 对象
`fdb_kv_get_blob` API 不同,该 API 在 get 过程中并不会执行 value 数据的读取动作。返回的 KV 对象中存放了读取回来的 KV 属性,该 API 适用于 value 长度不确定,或 value 长度过长,需要分段读取的场景。
`fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv)`
| 参数 | 描述 |
| ---- | ------------------------------------------------------------ |
| db | 数据库对象 |
| key | KV 的名称 |
| kv | 通过 KV 对象,返回 KV 的属性,可以再用 `fdb_kv_to_blob` 转换为 blob 对象,再进行数据读取 |
| 返回 | 错误码 |
#### 获取字符串类型 KV
**注意**
- 该函数不允许连续使用,使用时需使用 strdup 包裹,确保每次返回回来的字符串内存空间独立;
- 该函数不支持可重入,返回的值位于函数内部缓冲区,出于安全考虑,请加锁保护。
`char *fdb_kv_get(fdb_kvdb_t db, const char *key)`
| 参数 | 描述 |
| ---- | ----------------------------------- |
| db | 数据库对象 |
| key | KV 的名称 |
| 返回 | !=NULL: KV 的 valueNULL: 获取失败 |
### 删除 KV
`fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key)`
| 参数 | 描述 |
| ---- | ---------- |
| db | 数据库对象 |
| key | KV 的名称 |
| 返回 | 错误码 |
### 重置 KVDB
将 KVDB 中的 KV 重置为初始时的默认值
`fdb_err_t fdb_kv_set_default(fdb_kvdb_t db)`
| 参数 | 描述 |
| ---- | ---------- |
| db | 数据库对象 |
| 返回 | 错误码 |
### 打印 KVDB 中的 KV 信息
`void fdb_kv_print(fdb_kvdb_t db)`
| 参数 | 描述 |
| ---- | ---------- |
| db | 数据库对象 |
| 返回 | 错误码 |
### KV 对象转换为 blob 对象
`fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob)`
| 参数 | 描述 |
| ---- | ------------------ |
| kv | 待转换的 KV 对象 |
| blob | 转换前的 blob 对象 |
| 返回 | 转换后的 blob 对象 |
### 初始化 KV 迭代器
`fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr)`
| 参数 | 描述 |
| ---- | -------------------- |
| itr | 待初始化的迭代器对象 |
| 返回 | 初始化后的迭代器对象 |
### 迭代 KV
使用该迭代器 API可以遍历整个 KVDB 中的所有 KV。
> **注意**:使用前请先初始化迭代器
`bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr)`
[点击查看示例](zh-cn/sample-kvdb-traversal.md)
## TSDB
### 初始化 TSDB
`fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len, void *user_data)`
| 参数 | 描述 |
| --------- | ------------------------------- |
| db | 数据库对象 |
| name | 数据库名称 |
| part_name | 使用 FAL 分区表中的哪一个分区 |
| get_time | 获取当前时间戳的函数 |
| max_len | 每条 TSL 的最大长度 |
| user_data | 用户自定义数据,没有时传入 NULL |
| 返回 | 错误码 |
### 控制 TSDB
通过命令控制字,用户可以对数据库进行一些控制操作
`void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)`
| 参数 | 描述 |
| ---- | ---------- |
| db | 数据库对象 |
| cmd | 命令控制字 |
| arg | 控制的参数 |
| 返回 | 错误码 |
支持的命令控制字如下:
```C
#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x0 /**< 设置扇区大小 */
#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x1 /**< 获取扇区大小 */
#define FDB_TSDB_CTRL_SET_LOCK 0x2 /**< 设置加锁函数 */
#define FDB_TSDB_CTRL_SET_UNLOCK 0x3 /**< 设置解锁函数 */
#define FDB_TSDB_CTRL_SET_ROLLOVER 0x4 /**< 设置是否滚动写入默认滚动设置非滚动时如果数据库写满无法再追加写入 */
#define FDB_TSDB_CTRL_GET_ROLLOVER 0x5 /**< 获取是否滚动写入 */
#define FDB_TSDB_CTRL_GET_LAST_TIME 0x6 /**< 获取上次追加 TSL 时的时间戳 */
```
### 追加 TSL
对于 TSDB ,新增 TSL 的过程,就是往 TSDB 末尾追加新 TSL 的过程
`fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)`
| 参数 | 描述 |
| ---- | --------------------------- |
| db | 数据库对象 |
| blob | blob 对象,做为 TSL 的数据 |
| 返回 | 错误码 |
### 迭代 TSL
遍历整个 TSDB 并执行迭代回调
`void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg)`
| 参数 | 描述 |
| ------ | --------------------------------------- |
| db | 数据库对象 |
| cb | 回调函数,每次遍历到 TSL 时会执行该回调 |
| cb_arg | 回调函数的参数 |
| 返回 | 错误码 |
### 按时间段迭代 TSL
按时间段范围,遍历整个 TSDB 并执行迭代回调
`void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)`
| 参数 | 描述 |
| ------ | --------------------------------------- |
| db | 数据库对象 |
| from | 开始时间戳 |
| to | 结束时间戳 |
| cb | 回调函数,每次遍历到 TSL 时会执行该回调 |
| cb_arg | 回调函数的参数 |
| 返回 | 错误码 |
### 查询 TSL 的数量
按照传入的时间段,查询符合状态的 TSL 数量
`size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)`
| 参数 | 描述 |
| ------ | -------------- |
| db | 数据库对象 |
| from | 开始时间戳 |
| to | 结束时间戳 |
| status | TSL 的状态条件 |
| 返回 | 数量 |
### 设置 TSL 状态
`fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)`
| 参数 | 描述 |
| ------ | ------------ |
| db | 数据库对象 |
| tsl | TSL 对象 |
| status | TSL 的新状态 |
| 返回 | 错误码 |
### 清空 TSDB
`void fdb_tsl_clean(fdb_tsdb_t db)`
| 参数 | 描述 |
| ---- | ---------- |
| db | 数据库对象 |
| 返回 | 错误码 |
### TSL 对象转换为 blob 对象
`fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)`
| 参数 | 描述 |
| ---- | ------------------ |
| tsl | 待转换的 TSL 对象 |
| blob | 转换前的 blob 对象 |
| 返回 | 转换后的 blob 对象 |

View File

@ -0,0 +1,41 @@
# 配置说明
FlashDB 的使用时,可以通过 fdb_cfg.h 对其进行功能配置,该文件模板位于 `inc` 目录下,也可以去具体的 demo 工程里拷贝。下面详细介绍一下配置详情
## FDB_USING_KVDB
使能 KVDB 功能
### FDB_KV_AUTO_UPDATE
使能 KV 自动升级功能。该功能使能后, `fdb_kvdb.ver_num` 存储了当前数据库的版本,如果版本发生变化时,会自动触发升级动作,将更新新的默认 KV 集合至当前数据库中。
## FDB_USING_TSDB
使能 TSDB 功能
## FDB_WRITE_GRAN
Flash 写粒度,单位为 bit。目前支持
- 1: nor flash
- 8: stm32f2/f4 片上 Flash
- 32: stm32f1 片上 Flash
如果数据库中使用了多种 Flash 规格,例如:既有 nor flash也有 stm32f4 片上 Flash 此时取最大值作为配置项8 bit
## FDB_BIG_ENDIAN
MCU 大小端配置,默认不配置时,系统自动使用小端配置
## FDB_PRINT(...)
打印函数宏定义配置,默认不配置时,使用 printf 作为打印日志是输出函数。用户也可以自定义新的打印函数宏定义,例如:
```C
#define FDB_PRINT(...) my_printf(__VA_ARGS__)
```
## FDB_DEBUG_ENABLE
使能调试信息输出。关闭该配置时,系统将不会输出用于调试的日志。

209
docs/zh-cn/demo-details.md Normal file
View File

@ -0,0 +1,209 @@
# 演示工程详细说明
## 功能代码说明
演示工程主要演示了 FlashDB 从初始化,到示例运行的过程。
### main.c
演示工程中,`main.c` 中的 `main 函数` 为入口函数,该函数分为两段,分别初始化了一个 KVDB 和 TSDB 对象,然后执行与之对应的示例函数,大致内容如下:
```C
#ifdef FDB_USING_KVDB
{ /* KVDB Sample */
struct fdb_default_kv default_kv;
default_kv.kvs = default_kv_table;
default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
/* set the lock and unlock function if you want */
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, lock);
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, unlock);
/* Key-Value database initialization
*
* &kvdb: database object
* "env": database name
* "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
* Please change to YOUR partition name.
* &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
* NULL: The user data if you need, now is empty.
*/
result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL);
if (result != FDB_NO_ERR) {
return -1;
}
/* run basic KV samples */
kvdb_basic_sample(&kvdb);
/* run string KV samples */
kvdb_type_string_sample(&kvdb);
/* run blob KV samples */
kvdb_type_blob_sample(&kvdb);
}
#endif /* FDB_USING_KVDB */
#ifdef FDB_USING_TSDB
{ /* TSDB Sample */
/* set the lock and unlock function if you want */
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, lock);
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, unlock);
/* Time series database initialization
*
* &tsdb: database object
* "log": database name
* "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
* Please change to YOUR partition name.
* get_time: The get current timestamp function.
* 128: maximum length of each log
* NULL: The user data if you need, now is empty.
*/
result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, NULL);
/* read last saved time for simulated timestamp */
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts);
if (result != FDB_NO_ERR) {
return -1;
}
/* run TSDB sample */
tsdb_sample(&tsdb);
}
#endif /* FDB_USING_TSDB */
```
#### 设置加锁与解锁
初始化 KVDB 及 TSDB 前通常需要通过 `control` 函数设置 `加锁回调``解锁回调`
- KVDB
- fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, lock);
- fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, unlock);
- TSDB
- fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, lock);
- fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, unlock);
对于裸机平台,加锁与解锁回调通常设置为关中断与开中断函数。而 RTOS 平台一般使用 mutex 互斥锁或 二值信号量 的 take 及 release 动作作为加锁与解锁的方式。
#### 模拟时间戳
对于 TSDB正常项目中的时间戳应当通过 RTC 或者网络时钟方式来获取,但是这里为了增强演示工程的通用性,使用 `fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts);` 获取 TSDB 上次使用的时间戳,存入 `counts` 。每次使用 `get_time` 获取当前时间时,会对 `counts` 进行加一处理,模拟时间往前走的动作,避免重复。
所以,使用这种方法模拟的时间戳没有实时时间的含义,只是为了让每条记录插入的时间戳不重复。
#### 示例
本文主要讲解初始化相关代码功能,更多关于示例函数的详细讲解,请阅读对应的示例详解。
| 示例函数 | 说明 | 详解 |
| ------------------------------ | ------------------ | ----------------------------------------- |
| kvdb_basic_sample(&kvdb) | KVDB 基础示例 | [点击查看](zh-cn/sample-kvdb-basic) |
| kvdb_type_string_sample(&kvdb) | 字符串类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-string) |
| kvdb_type_blob_sample(&kvdb) | blob 类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-blob) |
| tsdb_sample(&tsdb) | TSDB 基础示例 | [点击查看](zh-cn/sample-tsdb-basic) |
## 首次运行日志
下面将对日志进行分段讲解。
### FAL 初始化
FAL 初始化时会打印 Flash 设备信息及分区表信息。
```
[D/FAL] (fal_flash_init:65) Flash device | stm32_onchip | addr: 0x08000000 | len: 0x00040000 | blk_size: 0x00000800 |initialized finish.
[I/FAL] ==================== FAL partition table ====================
[I/FAL] | name | flash_dev | offset | length |
[I/FAL] -------------------------------------------------------------
[I/FAL] | fdb_tsdb1 | stm32_onchip | 0x0001a000 | 0x00002000 |
[I/FAL] | fdb_kvdb1 | stm32_onchip | 0x0001c000 | 0x00004000 |
[I/FAL] =============================================================
[I/FAL] Flash Abstraction Layer (V0.5.0) initialize success.
```
### KVDB 初始化
KVDB 每次初始化时会检查扇区头部信息是否正确(扇区头部存储了一些属性信息),如果不正确,将会自动格式化该扇区。
Flash 首次使用时,通常需要格式化处理,所以首次初始化时的日志会含有格式化的信息。格式化成功后,后续每次初始化无需再次格式化。
```
[FlashDB][kv][env] (src/fdb_kvdb.c:1599) KVDB in partition fdb_kvdb1, size is 16384 bytes.
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00000000).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00000800).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00001000).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00001800).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00002000).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00002800).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00003000).
[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00003800).
[FlashDB][kv][env] All sector header is incorrect. Set it to default.
[FlashDB] FlashDB V1.0.0 beta is initialize success.
[FlashDB] You can get the latest version on https://github.com/armink/FlashDB .
```
### TSDB 初始化
与 KVDB 类似TSDB 首次初始化时,也会自动执行格式化动作。
```
[FlashDB][tsl][log] Sector (0x00000000) header info is incorrect.
[FlashDB][tsl][log] All sector format finished.
[FlashDB][tsl][log] (src/fdb_tsdb.c:759) TSDB (log) oldest sectors is 0x00000000, current using sector is 0x00000000.
```
### 运行示例
日志详解,请阅读 [示例文档](zh-cn/sample-kvdb-basic)
```
[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ====================
[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 0
[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 1
[FlashDB][sample][kvdb][basic] ===========================================================
[FlashDB][sample][kvdb][string] ==================== kvdb_type_string_sample ====================
[FlashDB][sample][kvdb][string] create the 'temp' string KV, value is: 36C
[FlashDB][sample][kvdb][string] get the 'temp' value is: 36C
[FlashDB][sample][kvdb][string] set 'temp' value to 38C
[FlashDB][sample][kvdb][string] delete the 'temp' finish
[FlashDB][sample][kvdb][string] ===========================================================
[FlashDB][sample][kvdb][blob] ==================== kvdb_type_blob_sample ====================
[FlashDB][sample][kvdb][blob] create the 'temp' blob KV, value is: 36
[FlashDB][sample][kvdb][blob] get the 'temp' value is: 36
[FlashDB][sample][kvdb][blob] set 'temp' value to 38
[FlashDB][sample][kvdb][blob] delete the 'temp' finish
[FlashDB][sample][kvdb][blob] ===========================================================
省略 TSDB 初始化日志……
[FlashDB][sample][tsdb] ==================== tsdb_sample ====================
[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85)
[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90)
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] query count is: 2
[FlashDB][sample][tsdb] set the TSL (time 1) status from 2 to 3
[FlashDB][sample][tsdb] set the TSL (time 2) status from 2 to 3
[FlashDB][sample][tsdb] ===========================================================
```
## 二次运行日志
这里主要看下 KVDB 与 TSDB 初始化的日志,对比首次初始化可以发现,第二次启动时的初始化日志会相对少很多,主要少了格式化的过程。
```
[D/FAL] (fal_flash_init:65) Flash device | stm32_onchip | addr: 0x08000000 | len: 0x00040000 | blk_size: 0x00000800 |initialized finish.
[I/FAL] ==================== FAL partition table ====================
[I/FAL] | name | flash_dev | offset | length |
[I/FAL] -------------------------------------------------------------
[I/FAL] | fdb_tsdb1 | stm32_onchip | 0x0001a000 | 0x00002000 |
[I/FAL] | fdb_kvdb1 | stm32_onchip | 0x0001c000 | 0x00004000 |
[I/FAL] =============================================================
[I/FAL] Flash Abstraction Layer (V0.5.0) initialize success.
[FlashDB][kv][env] (D:/Program/STM32/FlashDB/src/fdb_kvdb.c:1599) KVDB in partition fdb_kvdb1, size is 16384 bytes.
[FlashDB] FlashDB V1.0.0 beta is initialize success.
[FlashDB] You can get the latest version on https://github.com/armink/FlashDB .
省略示例运行日志……
[FlashDB][tsl][log] (D:/Program/STM32/FlashDB/src/fdb_tsdb.c:759) TSDB (log) oldest sectors is 0x00000000, current using sector is 0x00000000.
省略示例运行日志……
```

View File

@ -0,0 +1,36 @@
# 基于 STM32F10X 片上 flash 的演示
## 介绍
基于 STM32F10X 芯片片上 Flash 的 KVDB 及 TSDB 演示
- MCU: STM32F103VE
- Flash Driver
- STM32 on-chip flash
- IO
- UART
- TXD: PA9
- RXD: PA10
## 使用
### 步骤1连接串口助手或终端
将开发板的串口连接至 PC并打开串口助手或终端工具
### 步骤2打开演示工程
支持下面两种 IDE
- Keil MDK打开 `RVMDK\FlashDB.uvprojx`
- [RT-Studio](https://www.rt-thread.org/page/studio.html): 通过工程向导进行导入
### 步骤3编译与下载
编译演示工程,如果编译成功,下载固件至开发板
### 步骤4检查日志结果
演示工程的运行结果,将会输出至 PC 的串口工具上。
> PS: 演示工程代码及日志分析,详见:入门 -> [演示说明文档](zh-cn/demo-details.md)

View File

@ -0,0 +1,42 @@
# 基于 STM32F40X 片外 SPI-Flash 的演示
## 介绍
基于 STM32F40X 芯片片上 Flash 的 KVDB 及 TSDB 演示
- MCU: STM32F405RG
- Flash Driver
- spi flash (W25Q64) by using [SFUD](https://github.com/armink/SFUD)
- IO
- UART
- TXD: PA9
- RXD: PA10
- SPI
- CS: PB12 (software CS)
- SCK: PB13
- MISO: PB14
- MOSI: PB15
## 使用
### 步骤1连接串口助手或终端
将开发板的串口连接至 PC并打开串口助手或终端工具
### 步骤2打开演示工程
支持下面两种 IDE
- Keil MDK打开 `RVMDK\FlashDB.uvprojx`
- [RT-Studio](https://www.rt-thread.org/page/studio.html): 通过工程向导进行导入
### 步骤3编译与下载
编译演示工程,如果编译成功,下载固件至开发板
### 步骤4检查日志结果
演示工程的运行结果,将会输出至 PC 的串口工具上。
> PS: 演示工程代码及日志分析,详见:入门 -> [演示说明文档](zh-cn/demo-details.md)

View File

@ -0,0 +1,37 @@
# 基于 STM32F40X 片上 flash 的演示
## 介绍
基于 STM32F40X 芯片片上 Flash 的 KVDB 及 TSDB 演示
- MCU: STM32F405RG
- Flash Driver
- STM32 on-chip flash
- IO
- UART
- TXD: PA9
- RXD: PA10
## 使用
### 步骤1连接串口助手或终端
将开发板的串口连接至 PC并打开串口助手或终端工具
### 步骤2打开演示工程
支持下面两种 IDE
- Keil MDK打开 `RVMDK\FlashDB.uvprojx`
- [RT-Studio](https://www.rt-thread.org/page/studio.html): 通过工程向导进行导入
### 步骤3编译与下载
编译演示工程,如果编译成功,下载固件至开发板
### 步骤4检查日志结果
演示工程的运行结果,将会输出至 PC 的串口工具上。
> PS: 演示工程代码及日志分析,详见:入门 -> [演示说明文档](zh-cn/demo-details.md)

145
docs/zh-cn/porting.md Normal file
View File

@ -0,0 +1,145 @@
# 移植指南
FlashDB 底层的 Flash 管理及操作依赖于 RT-Thread 的 FAL (Flash Abstraction Layer) Flash 抽象层开源软件包 ,该开源库也支持运行在 **裸机平台** [(点击查看介绍)](http://packages.rt-thread.org/detail.html?package=fal)。所以只需要将所用到的 Flash 对接到 FAL ,即可完成整个移植工作。
## 移植介绍
![flashdb_porting_layer](_media/flashdb_porting_layer.png)
移植的主要工作都在 FAL 这边,其他接口并不是强依赖,可以根据自己的情况进行对接。
移植前建议先了解下 FAL 功能介绍详见http://packages.rt-thread.org/detail.html?package=fal
FAL 底层将不同的 Flash 存储介质进行了统一封装,并提供了分区表机制,暴露给上层用户。
FlashDB 的每个数据库就是基于 FAL 提供的分区机制,每个数据库都坐落在某个 FAL 的分区上,相当于一个分区对应一个数据库。
下面将详细讲解 FAL 的移植流程,更多移植演示可以参考已经提供的 demo 。
## FAL 移植
### 定义 flash 设备
在定义 Flash 设备表前,需要先定义 Flash 设备。可以是片内 flash, 也可以是片外基于 SFUD 的 spi flash
- 定义片内 flash 设备可以参考 [`fal_flash_stm32f2_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_stm32f2_port.c) 。
- 定义片外 spi flash 设备可以参考 [`fal_flash_sfud_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_sfud_port.c) 。
定义具体的 Flash 设备对象,用户需要根据自己的 Flash 情况分别实现 `init``read``write``erase` 这些操作函数:
- `static int init(void)`**可选** 的初始化操作。
- `static int read(long offset, uint8_t *buf, size_t size)`:读取操作。
| 参数 | 描述 |
| ------ | ------------------------- |
| offset | 读取数据的 Flash 偏移地址 |
| buf | 存放待读取数据的缓冲区 |
| size | 待读取数据的大小 |
| return | 返回实际读取的数据大小 |
- `static int write(long offset, const uint8_t *buf, size_t size)` :写入操作。
| 参数 | 描述 |
| ------ | ------------------------- |
| offset | 写入数据的 Flash 偏移地址 |
| buf | 存放待写入数据的缓冲区 |
| size | 待写入数据的大小 |
| return | 返回实际写入的数据大小 |
- `static int erase(long offset, size_t size)` :擦除操作。
| 参数 | 描述 |
| ------ | ------------------------- |
| offset | 擦除区域的 Flash 偏移地址 |
| size | 擦除区域的大小 |
| return | 返回实际擦除的区域大小 |
用户需要根据自己的 Flash 情况分别实现这些操作函数。在文件最底部定义了具体的 Flash 设备对象 ,如下示例定义了 stm32f2 片上 flashstm32f2_onchip_flash
```C
const struct fal_flash_dev stm32f2_onchip_flash =
{
.name = "stm32_onchip",
.addr = 0x08000000,
.len = 1024*1024,
.blk_size = 128*1024,
.ops = {init, read, write, erase},
.write_gran = 8
};
```
- `"stm32_onchip"` : Flash 设备的名字。
- `0x08000000`: 对 Flash 操作的起始地址。
- `1024*1024`Flash 的总大小1MB
- `128*1024`Flash 块/扇区大小(因为 STM32F2 各块大小不均匀所以擦除粒度为最大块的大小128K
- `{init, read, write, erase}` Flash 的操作函数。 如果没有 init 初始化过程,第一个操作函数位置可以置空。
- `8` : 设置写粒度,单位 bit 0 表示未生效(默认值为 0 ),该成员是 fal 版本大于 0.4.0 的新增成员。各个 flash 写入粒度不尽相同,可通过该成员进行设置,以下列举几种常见 Flash 写粒度:
- nor flash: 1 bit
- stm32f2/f4: 8 bit
- stm32f1: 32 bit
- stm32l4: 64 bit
### 定义 flash 设备表
Flash 设备表定义在 `fal_cfg.h` 头文件中,定义分区表前需 **新建 `fal_cfg.h` 文件** ,请将该文件统一放在对应 BSP 或工程目录的 port 文件夹下并将该头文件路径加入到工程。fal_cfg.h 可以参考 [示例文件 fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h) 完成。
设备表示例:
```C
/* ===================== Flash device Configuration ========================= */
extern const struct fal_flash_dev stm32f2_onchip_flash;
extern struct fal_flash_dev nor_flash0;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&stm32f2_onchip_flash, \
&nor_flash0, \
}
```
Flash 设备表中,有两个 Flash 对象,一个为 STM32F2 的片内 Flash ,一个为片外的 Nor Flash。
### 定义 flash 分区表
分区表也定义在 `fal_cfg.h` 头文件中。Flash 分区基于 Flash 设备,每个 Flash 设备又可以有 N 个分区,这些分区的集合就是分区表。在配置分区表前,务必保证已定义好 **Flash 设备****设备表**。fal_cfg.h 可以参考 [示例文件 fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h) 完成。
分区表示例:
```C
#define NOR_FLASH_DEV_NAME "norflash0"
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \
{FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \
{FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
```
上面这个分区表详细描述信息如下:
| 分区名 | Flash 设备名 | 偏移地址 | 大小 | 说明 |
| ----------- | -------------- | --------- | ----- | ------------------ |
| "bl" | "stm32_onchip" | 0 | 64KB | 引导程序 |
| "app" | "stm32_onchip" | 64*1024 | 704KB | 应用程序 |
| "easyflash" | "norflash0" | 0 | 1MB | EasyFlash 参数存储 |
| "download" | "norflash0" | 1024*1024 | 1MB | OTA 下载区 |
用户需要修改的分区参数包括:分区名称、关联的 Flash 设备名、偏移地址(相对 Flash 设备内部)、大小,需要注意以下几点:
- 分区名保证 **不能重复**
- 关联的 Flash 设备 **务必已经在 Flash 设备表中定义好** ,并且 **名称一致** ,否则会出现无法找到 Flash 设备的错误;
- 分区的起始地址和大小 **不能超过 Flash 设备的地址范围** ,否则会导致包初始化错误;
> 注意:每个分区定义时,除了填写上面介绍的参数属性外,需在前面增加 `FAL_PART_MAGIC_WORD` 属性,末尾增加 `0` (目前用于保留功能)

View File

@ -0,0 +1,63 @@
# 快速开始
本文档将帮助用户快速的将 FlashDB 在演示平台上使用起来,体验 FlashDB 的实际使用效果
## 基本概念
- **键值数据库KVDB**是一种非关系数据库它将数据存储为键值Key-Value对集合其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。
- **时序数据TSDB** :时间序列数据库 Time Series Database , 简称 TSDB它将数据按照 **时间顺序存储** 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。
- **时序记录TSL** TSL (Time series log),是 TSDB 中每条记录的简称。
- **Blob** 在计算机中blob 常常是数据库中用来存储二进制文件的字段类型。在 FlashDB 中, KV 和 TSL 都使用 blob 类型来存储,该类型可以兼容任意变量类型。
- **迭代器iterator**:它可以让用户透过特定的接口巡访容器中的每一个元素,而不用了解底层的实现。 TSDB 和 KVDB 都支持通过迭代器对数据库进行遍历访问。
## 功能框图
通过下面的功能框图,可以大致了解 FlashDB 功能模块划分
![flashdb_framework](_media/flashdb_framework.png)
## 准备开发环境
使用前,需提前在 PC 上安装下面的开发软件
### 集成开发环境
FlashDB 提供的演示工程默认支持两种工程:
- **RT-Studio** 免费使用支持中文使用方便下载地址https://www.rt-thread.org/page/studio.html
- **Keil MDK** :需安装 MDK v5 版本的集成开发环境
### 串口工具
准备 `串口调试助手``串口终端工具` ,后续需要在查看运行日志时使用
## 获取源码
最新代码目前托管在 GitHub 及 Gitee master 分支为开发版本,推荐下载已发布的版本
- GitHub 下载https://github.com/armink/FlashDB/releases
- Gitee 下载https://gitee.com/armink/FlashDB/releases
## 选择演示平台
在项目的 `demos` 目录下,目前已提供下面一些硬件演示平台,可以选择一个硬件平台,真机体验一下 FlashDB 的运行过程。
更多详细介绍,点击下方表格中的 **使用说明** 进行查看。
| 硬件平台 | 路径 | flash 类型 | 使用说明 |
| --------------------- | :---------------------------- | :------------ | ----------------------------------------------- |
| stm32f10x | `demos/stm32f103ve` | stm32 on-chip | [点击查看](zh-cn/demo-stm32f103ve.md) |
| stm32f40x | `demos/stm32f405rg` | stm32 on-chip | [点击查看](zh-cn/demo-stm32f405rg.md) |
| stm32f40x + spi flash | `demos/stm32f405rg_spi_flash` | spi flash | [点击查看](zh-cn/demo-stm32f405rg-spi-flash.md) |
## 查看示例说明
如果上面没有合适自己的演示平台,也可以先查看一下感兴趣的示例说明。
| 示例文件 | 说明 | 详解 |
| ----------------------------------- | ------------------ | ----------------------------------------- |
| `samples/kvdb_basic_sample.c` | KVDB 基础示例 | [点击查看](zh-cn/sample-kvdb-basic) |
| `samples/kvdb_type_string_sample.c` | 字符串类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-string) |
| `samples/kvdb_type_blob_sample.c` | blob 类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-blob) |
| `samples/tsdb_sample.c` | TSDB 基础示例 | [点击查看](zh-cn/sample-tsdb-basic) |

View File

@ -0,0 +1,60 @@
# KVDB 基础示例
该示例主要演示了 KVDB 的基础功能,包括 KV 的获取及设置修改功能。
## 代码说明
示例代码位于 `samples/kvdb_basic_sample.c` ,在 `main.c` 有定义默认的 KV 集合表,在里面有 `boot_count` KV。通过该 KV 来记录当前系统的启动次数。每次掉电再启动时,该 KV 会自动加一,并保存至 KVDB 中。大致内容如下:
```C
void kvdb_basic_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
int boot_count = 0;
FDB_INFO("==================== kvdb_basic_sample ====================\n");
{ /* GET the KV value */
/* get the "boot_count" KV value */
fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
} else {
FDB_INFO("get the 'boot_count' failed\n");
}
}
{ /* CHANGE the KV value */
/* increase the boot count */
boot_count ++;
/* change the "boot_count" KV's value */
fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
}
FDB_INFO("===========================================================\n");
}
```
## 首次运行日志
当前 `boot_count` 为 0 ,对其加一后,存入数据库。
```
[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ====================
[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 0
[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 1
[FlashDB][sample][kvdb][basic] ===========================================================
```
## 二次运行日志
当前 `boot_count` 为 1 ,说明上次的保存生效了,再对其加一后保存,供下次访问使用。
```
[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ====================
[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 1
[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 2
[FlashDB][sample][kvdb][basic] ===========================================================
```

View File

@ -0,0 +1,38 @@
# 遍历所有 KV
本示例演示了如果遍历 KVDB 中的所有 KV ,用户可以在遍历 KV 时增加自己的处理动作。
## 代码说明
下面的示例代码中,首先初始化了 KVDB 的迭代器,然后使用迭代器 API ,将 KVDB 的所有 KV 逐一遍历出来。
遍历出来的 KV 对象含有 KV 的一些属性包括key name, value saved addr, value length 等,用户通过 `fdb_blob_read` 配合 `fdb_kv_to_blob` 读取出来,做一些自己的业务处理。
```C
void kvdb_tarversal_sample(fdb_kvdb_t kvdb)
{
struct fdb_kv_iterator iterator;
fdb_kv_t cur_kv;
struct fdb_blob blob;
size_t data_size;
uint8_t *data_buf;
fdb_kv_iterator_init(&iterator);
while (fdb_kv_iterate(kvdb, &iterator)) {
cur_kv = &(iterator.curr_kv);
data_size = (size_t) cur_kv->value_len;
data_buf = (uint8_t *) malloc(data_size);
if (data_buf == NULL) {
FDB_INFO("Error: malloc failed.\n");
break;
}
fdb_blob_read((fdb_db_t) kvdb, fdb_kv_to_blob(cur_kv, fdb_blob_make(&blob, data_buf, data_size)));
/*
* balabala do what ever you like with blob...
*/
free(data_buf);
}
}
```

View File

@ -0,0 +1,71 @@
# blob 类型 KV 示例
该示例主要演示了blob KV 的相关功能blob KV 是一个比较常用类型,其 value 是一个没有类型限制的二进制类型。在功能上blob KV 也兼容字符串 KV 。在 API 的使用上, blob KV 拥有一套独立的 API ,可以很快速的实现各种类型 KV 到 KVDB 中的存储,比如:基本类型,数组以及结构体等。
## 代码说明
示例代码位于 `samples/kvdb_type_blob.c` ,使用一个名为 `"temp"` 的 KV 来存储温度值,分别演示了 blob KV 从 `创建->读取->修改->删除` 的全过程。大致内容如下:
```C
void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
FDB_INFO("==================== kvdb_type_blob_sample ====================\n");
{ /* CREATE new Key-Value */
int temp_data = 36;
/* It will create new KV node when "temp" KV not in database.
* fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
*/
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data);
}
{ /* GET the KV value */
int temp_data = 0;
/* get the "temp" KV value */
fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'temp' value is: %d\n", temp_data);
}
}
{ /* CHANGE the KV value */
int temp_data = 38;
/* change the "temp" KV's value to 38 */
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
FDB_INFO("set 'temp' value to %d\n", temp_data);
}
{ /* DELETE the KV by name */
fdb_kv_del(kvdb, "temp");
FDB_INFO("delete the 'temp' finish\n");
}
FDB_INFO("===========================================================\n");
}
```
## 运行日志
通过日志可以看出:
- 首先创建了一个 KV 名为 `"temp"` ,并给予初值 36℃
- 读取 `"temp"` KV 当前的值,发现与初值相同
- 修改 `"temp"` KV 的值为 38℃
- 最后删除 `"temp"` KV
```
[FlashDB][sample][kvdb][blob] ==================== kvdb_type_blob_sample ====================
[FlashDB][sample][kvdb][blob] create the 'temp' blob KV, value is: 36
[FlashDB][sample][kvdb][blob] get the 'temp' value is: 36
[FlashDB][sample][kvdb][blob] set 'temp' value to 38
[FlashDB][sample][kvdb][blob] delete the 'temp' finish
[FlashDB][sample][kvdb][blob] ===========================================================
```

View File

@ -0,0 +1,69 @@
# 字符串类型 KV 示例
该示例主要演示了字符串 KV 的相关功能,字符串 KV 作为一个特殊的 KV 类型,其 Key 与 Value 均为字符串,常被用于参数存储、命令存储等可读性要求较高的场景。
## 代码说明
示例代码位于 `samples/kvdb_type_string.c` ,使用一个名为 `"temp"` 的 KV 来存储温度值,分别演示了字符串 KV 从 `创建->读取->修改->删除` 的全过程。大致内容如下:
```C
void kvdb_type_string_sample(fdb_kvdb_t kvdb)
{
FDB_INFO("==================== kvdb_type_string_sample ====================\n");
{ /* CREATE new Key-Value */
char temp_data[10] = "36C";
/* It will create new KV node when "temp" KV not in database. */
fdb_kv_set(kvdb, "temp", temp_data);
FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data);
}
{ /* GET the KV value */
char *return_value, temp_data[10] = { 0 };
/* Get the "temp" KV value.
* NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
*/
return_value = fdb_kv_get(kvdb, "temp");
/* the return value is NULL when get the value failed */
if (return_value != NULL) {
strncpy(temp_data, return_value, sizeof(temp_data));
FDB_INFO("get the 'temp' value is: %s\n", temp_data);
}
}
{ /* CHANGE the KV value */
char temp_data[10] = "38C";
/* change the "temp" KV's value to "38.1" */
fdb_kv_set(kvdb, "temp", temp_data);
FDB_INFO("set 'temp' value to %s\n", temp_data);
}
{ /* DELETE the KV by name */
fdb_kv_del(kvdb, "temp");
FDB_INFO("delete the 'temp' finish\n");
}
FDB_INFO("===========================================================\n");
}
```
## 运行日志
通过日志可以看出:
- 首先创建了一个 KV 名为 `"temp"` ,并给予初值 36℃
- 读取 `"temp"` KV 当前的值,发现与初值相同
- 修改 `"temp"` KV 的值为 38℃
- 最后删除 `"temp"` KV
```
[FlashDB][sample][kvdb][string] ==================== kvdb_type_string_sample ====================
[FlashDB][sample][kvdb][string] create the 'temp' string KV, value is: 36C
[FlashDB][sample][kvdb][string] get the 'temp' value is: 36C
[FlashDB][sample][kvdb][string] set 'temp' value to 38C
[FlashDB][sample][kvdb][string] delete the 'temp' finish
[FlashDB][sample][kvdb][string] ===========================================================
```

View File

@ -0,0 +1,169 @@
# TSDB 基础示例
该示例主要演示了 TSDB 的基础功能,包括 TSL时序记录的追加、查询及状态修改功能。
## 代码说明
示例代码位于 `samples/tsdb_sample.c` ,包含追加、查询及状态修改这几个过程,大致代码如下:
```C
void tsdb_sample(fdb_tsdb_t tsdb)
{
struct fdb_blob blob;
FDB_INFO("==================== tsdb_sample ====================\n");
{ /* APPEND new TSL (time series log) */
struct env_status status;
/* append new log to TSDB */
status.temp = 36;
status.humi = 85;
fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
status.temp = 38;
status.humi = 90;
fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
}
{ /* QUERY the TSDB */
/* query all TSL in TSDB by iterator */
fdb_tsl_iter(tsdb, query_cb, tsdb);
}
{ /* QUERY the TSDB by time */
/* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
size_t count;
/* query all TSL in TSDB by time */
fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb);
/* query all FDB_TSL_WRITE status TSL's count in TSDB by time */
count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE);
FDB_INFO("query count is: %u\n", count);
}
{ /* SET the TSL status */
/* Change the TSL status by iterator or time iterator
* set_status_cb: the change operation will in this callback
*
* NOTE: The actions to modify the state must be in orderC.
* like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
* The intermediate states can also be ignored.
* such as: FDB_TSL_WRITE -> FDB_TSL_DELETED
*/
fdb_tsl_iter(tsdb, set_status_cb, tsdb);
}
FDB_INFO("===========================================================\n");
}
```
分别来看下这几个过程
- **追加**:分两次修改结构体对象 `status` 的值然后追加到 TSDB 中;
- **查询**:通过 TSDB 的迭代器 API ,在每次迭代时会自动执行 `query_cb` 回调函数,实现对 TSDB 中所有记录的查询,回调函数内容如下:
```C
static bool query_cb(fdb_tsl_t tsl, void *arg)
{
struct fdb_blob blob;
struct env_status status;
fdb_tsdb_t db = arg;
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
return false;
}
```
- **按时间查询**TSDB 还提供了按时间迭代的 API : `fdb_tsl_iter_by_time` ,可以传入起始和截至时间,此时迭代器会按照传入的时间段,对时序记录进行迭代。每次迭代时会执行 `query_by_time_cb` 回调,在回调中读取当前记录的内容,并打印出来。
```C
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg)
{
struct fdb_blob blob;
struct env_status status;
fdb_tsdb_t db = arg;
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
return false;
}
```
- **修改状态**:每条 TSL 在被追加到 TSDB 后,都可以修改其状态,状态共有 4 种:
- `FDB_TSL_WRITE`已写入状态TSL 被追加到 TSDB 中后的默认状态;
- `FDB_TSL_USER_STATUS1 `:该状态介于写入与删除之间,用户可自定义其状态含义,比如:数据已被同步至云端;
- `FDB_TSL_DELETED` :已删除状态,当 TSL 需要删除时,修改 TSL 的状态为该状态即可;
> 提示:在 FlashDB 中,为了提升 Flash 寿命,删除动作并不会真正的将数据从 Flash 从擦除,而是将其标记为删除状态,用户可以通过状态对不同的数据记录进行区分。
- `FDB_TSL_USER_STATUS2`:删除状态之后的自定义状态,预留给用户使用;
修改状态时只能按照 `FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2` 顺序进行修改,不能逆序修改。也可以跳过中间状态,例如:从 `FDB_TSL_WRITE` 直接修改为`FDB_TSL_DELETED` 状态,跳过 `FDB_TSL_USER_STATUS1` 状态。
示例中通过迭代器将当前所有的 TSL 都修改为 `FDB_TSL_USER_STATUS1` 状态,迭代器中的回调代码如下:
```C
static bool set_status_cb(fdb_tsl_t tsl, void *arg)
{
fdb_tsdb_t db = arg;
FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1);
fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
return false;
}
```
## 首次运行日志
通过日志可看出,示例首先追加了两条 TSL ,每条 TSL 分别存放了不同的温度及湿度记录。然后再通过普通查询和按时间查询方式,将 TSDB 中的 TSL 获取出来,最后修改其状态,从 `2: FDB_TSL_WRITE``3: FDB_TSL_USER_STATUS1`
```
[FlashDB][sample][tsdb] ==================== tsdb_sample ====================
[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85)
[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90)
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] query count is: 2
[FlashDB][sample][tsdb] set the TSL (time 1) status from 2 to 3
[FlashDB][sample][tsdb] set the TSL (time 2) status from 2 to 3
[FlashDB][sample][tsdb] ===========================================================
```
## 二次运行日志
第二次运行时,依旧会追加两条 TSL 。再看查询的结果,共计 4 条,包含了 2 条首次运行时追加的 TSL。通过打印的 time 时间戳可以看出,模拟时间戳工作正常。`query count is: 2` 说明了 TSDB 虽然有 4 条记录,但只有 2 条记录是写入状态。
```
[FlashDB][sample][tsdb] ==================== tsdb_sample ====================
[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85)
[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90)
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 3, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 4, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 3, temp: 36, humi: 85
[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 4, temp: 38, humi: 90
[FlashDB][sample][tsdb] query count is: 2
[FlashDB][sample][tsdb] set the TSL (time 1) status from 3 to 3
[FlashDB][sample][tsdb] set the TSL (time 2) status from 3 to 3
[FlashDB][sample][tsdb] set the TSL (time 3) status from 2 to 3
[FlashDB][sample][tsdb] set the TSL (time 4) status from 2 to 3
[FlashDB][sample][tsdb] ===========================================================
```

View File

@ -1,5 +0,0 @@
# FlashDB API 说明
---
马上就来……

View File

@ -1,3 +0,0 @@
# FlashDB 功能设计与实现
马上就来……

View File

@ -1,5 +0,0 @@
# FlashDB 移植说明
---
马上就来……

View File

@ -1,5 +0,0 @@
|文件名 |描述|
|:----- |:----|
|api.md |API 说明|
|port.md |移植说明|
|design.md |设计文档|