add embedded database lib (flashdb)

it is OK
This commit is contained in:
xuedongliang 2022-03-09 09:35:53 +08:00
commit 5ab2afe54e
40 changed files with 4682 additions and 5 deletions

View File

@ -18,5 +18,5 @@ menu "Applications"
source "$APP_DIR/Applications/control_app/Kconfig"
source "$APP_DIR/Applications/knowing_app/Kconfig"
source "$APP_DIR/Applications/sensor_app/Kconfig"
source "$APP_DIR/Applications/embedded_database_app/Kconfig"
endmenu

View File

@ -0,0 +1,6 @@
menuconfig USING_EMBEDDED_DATABASE_APP
bool "embedded database app"
default n
if USING_EMBEDDED_DATABASE_APP
source "$APP_DIR/Applications/embedded_database_app/flashdb_app/Kconfig"
endif

View File

@ -0,0 +1,14 @@
import os
Import('RTT_ROOT')
from building import *
cwd = GetCurrentDir()
objs = []
list = os.listdir(cwd)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(path, 'SConscript'))
Return('objs')

View File

@ -0,0 +1,5 @@
config EMBEDDED_DATABASE_FLASHDB_APP
bool "embedded database apps/flashdb(example)"
select USING_EMBEDDED_DATABASE
select USING_EMBEDDED_DATABASE_FLASHDB
default n

View File

@ -0,0 +1,9 @@
from building import *
cwd = GetCurrentDir()
src = Glob('*.c') + Glob('*.cpp')
CPPPATH = [cwd]
group = DefineGroup('flashdb(example)', src, depend = ['EMBEDDED_DATABASE_FLASHDB_APP'], LOCAL_CPPPATH = CPPPATH)
Return('group')

View File

@ -0,0 +1,129 @@
#include <transform.h>
#include <flashdb.h>
#define FDB_LOG_TAG "[flashdb_app]"
static pthread_mutex_t kv_locker, ts_locker;
static uint32_t boot_count = 0;
static time_t boot_time[10] = {0, 1, 2, 3};
/* default KV nodes */
static struct fdb_default_kv_node default_kv_table[] = {
{"username", "armink", 0}, /* string KV */
{"password", "123456", 0}, /* string KV */
{"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
{"boot_time", &boot_time, sizeof(boot_time)}, /* int array type KV */
};
/* KVDB object */
static struct fdb_kvdb kvdb = { 0 };
/* TSDB object */
struct fdb_tsdb tsdb = { 0 };
/* counts for simulated timestamp */
static int counts = 0;
extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
extern void tsdb_sample(fdb_tsdb_t tsdb);
static void lock(fdb_db_t db)
{
pthread_mutex_lock((pthread_mutex_t *)db->user_data);
}
static void unlock(fdb_db_t db)
{
pthread_mutex_unlock((pthread_mutex_t *)db->user_data);
}
static fdb_time_t get_time(void)
{
return time(NULL);
}
int flashdb_app(void)
{
fdb_err_t result;
bool file_mode = true;
uint32_t sec_size = 4096, db_size = sec_size * 4;
#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 */
pthread_mutex_init(&kv_locker, NULL);
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);
/* set the sector and database max size */
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
/* enable file mode */
fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
/* create database directory */
mkdir("fdb_kvdb1", 0777);
/* 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.
* &kv_locker: The locker object.
*/
result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, &kv_locker);
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 */
pthread_mutex_init(&ts_locker, NULL);
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, (void *)lock);
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, (void *)unlock);
/* set the sector and database max size */
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_SEC_SIZE, &sec_size);
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_MAX_SIZE, &db_size);
/* enable file mode */
fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_FILE_MODE, &file_mode);
/* create database directory */
mkdir("fdb_tsdb1", 0777);
/* 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
* ts_locker: The locker object.
*/
result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, &ts_locker);
/* 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 */
return 0;
}
#ifdef __RT_THREAD_H__
MSH_CMD_EXPORT(flashdb_app, flashdb test);
#endif

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief basic KV samples.
*
* basic Key-Value Database KV feature samples
* get and show currnet boot counts
*/
#include <flashdb.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][basic]"
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");
}
#endif /* FDB_USING_KVDB */

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief blob KV samples.
*
* Key-Value Database blob type KV feature samples
*/
#include <flashdb.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][blob]"
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");
}
#endif /* FDB_USING_KVDB */

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief string KV samples.
*
* Key-Value Database string type KV feature samples source file.
*/
#include <flashdb.h>
#include <string.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][string]"
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");
}
#endif /* FDB_USING_KVDB */

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief TSDB samples.
*
* Time series log (like TSDB) feature samples source file.
*
* TSL is time series log, the TSDB saved many TSLs.
*/
#include <flashdb.h>
#include <string.h>
#ifdef FDB_USING_TSDB
#define FDB_LOG_TAG "[sample][tsdb]"
struct env_status {
int temp;
int humi;
};
static bool query_cb(fdb_tsl_t tsl, void *arg);
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg);
static bool set_status_cb(fdb_tsl_t tsl, void *arg);
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: %zu\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 order.
* 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");
}
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;
}
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;
}
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;
}
#endif /* FDB_USING_TSDB */

View File

@ -11,6 +11,11 @@ menuconfig USING_CMSIS_5_DEMOAPP
select IMAGE_PROCESSING_USING_TJPGD
default n
config USING_CMSIS_5_NN_DEMOAPP_VEG_CLASSIFY
bool "Using CMSIS-5 NN demo app vegetable classify"
select USING_IMAGE_PROCESSING
select IMAGE_PROCESSING_USING_TJPGD
default n
endif

View File

@ -0,0 +1,37 @@
# CMSIS-NN vegetable classify example
This example uses CMSIS-NN to classify vegetable in real time under certain circumstances .
## Requirements:
- CMSIS-NN in Framework/knowing/cmsis_5
- **ov2640 need to be configured** in menuconfig "More Drivers->ov2640 driver" as follows
- Output format (RGB565 mode)
- (256) X direction resolution of outputimage
- (256) Y direction resolution of outputimage
- (512) X direction WINDOWS SIZE
- (512) Y direction WINDOWS SIZE
## To run this demo:
- Set up and configure the corresponding hardware environment.
- Run demo by type the command
```
cmsisnn_vegetable_classify
## Results
- **tomato**
![tomato](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/tomato.jpg)
- **potato**
![potato](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/potato.jpg)
- **pepper**
![pepper](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/pepper.jpg)
- **mushroom**
![mushroom](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/mushroom.jpg)

View File

@ -0,0 +1,18 @@
from building import *
import os
cwd = GetCurrentDir()
src = Split('''
model/nn_vegetable_classify.c
cmsisnn_vegetable_classify.c
''')
path = [
cwd + '/model',
cwd + '/demo'
]
group = DefineGroup('CMSISNN vegetable classify application', src, depend = ['USING_CMSIS_5_NN_DEMOAPP_VEG_CLASSIFY'], CPPPATH = path)
Return('group')

View File

@ -0,0 +1,188 @@
#include <rtthread.h>
#include <rtdevice.h>
#include "stdio.h"
#include "string.h"
#ifdef OV2640_RGB565_MODE
#ifdef RT_USING_POSIX
#include <dfs_posix.h>
#include <dfs_poll.h>
#ifdef RT_USING_POSIX_TERMIOS
#include <posix_termios.h>
#endif
#endif
#include <drv_ov2640.h>
#include <drv_lcd.h>
#include "nn_vegetable_classify.h"
#define JPEG_BUF_SIZE (2 * OV2640_X_RESOLUTION_IMAGE_OUTSIZE * OV2640_Y_RESOLUTION_IMAGE_OUTSIZE)
#define IOCTL_ERROR 1
static int fd = 0;
static int infer_times = 0;
static int photo_times = 0;
static int height = OV2640_X_RESOLUTION_IMAGE_OUTSIZE;
static int width = OV2640_Y_RESOLUTION_IMAGE_OUTSIZE;
static _ioctl_shoot_para shoot_para_t = {0};
const char *vegetable_label[] = {"mushroom", "pepper", "potato", "tomato"};
uint8_t *resized_buffer = NULL;
uint8_t *in_buffer = NULL;
int get_top_prediction_detection(q7_t *predictions)
{
int max_ind = 0;
int max_val = -128;
for (int i = 0; i < 10; i++)
{
if (max_val < predictions[i])
{
max_val = predictions[i];
max_ind = i;
}
}
return max_ind;
}
int cmsisnn_inference_vegetable_classify(uint8_t *input_data)
{
int8_t output_data[4];
char output[50] = {0};
char outputPrediction[50] = {0};
memset(output, 0, 50);
memset(outputPrediction, 0, 50);
run_nn_sn_classify((int8_t *)input_data, output_data);
arm_softmax_q7(output_data, 4, output_data);
infer_times++;
int top_ind = get_top_prediction_detection(output_data);
printf("times:%d Prediction:%s \r\n", infer_times, vegetable_label[top_ind]);
sprintf(outputPrediction, "times:%d Prediction:%s \r\n", infer_times, vegetable_label[top_ind]);
lcd_show_string(1, 280, 240, 16, 16, outputPrediction, RED);
return top_ind;
}
void resize_rgb888in_rgb565out(uint8_t *camera_image, uint16_t *resize_image)
{
uint8_t *psrc_temp = (uint8_t *)camera_image;
uint16_t *pdst_temp = (uint16_t *)resize_image;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
*pdst_temp++ = (*psrc_temp++ & 0xF8) << 8 | (*psrc_temp++ & 0xFC) << 3 | *psrc_temp++ >> 3;
}
}
}
void resize_rgb565in_rgb888out(uint8_t *camera_image, uint8_t *resize_image)
{
uint8_t *psrc_temp = (uint8_t *)camera_image;
uint8_t *pdst_temp = (uint8_t *)resize_image;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint8_t pixel_lo = *psrc_temp++;
uint8_t pixel_hi = *psrc_temp++;
*pdst_temp++ = (0xF8 & pixel_hi);
*pdst_temp++ = ((0x07 & pixel_hi) << 5) | ((0xE0 & pixel_lo) >> 3);
*pdst_temp++ = (0x1F & pixel_lo) << 3;
}
}
}
void lcd_show_ov2640_thread_detection(uint8_t *rgbbuffer)
{
int32_t ret = 0;
while (1)
{
ret = ioctl(fd, IOCTRL_CAMERA_START_SHOT, &shoot_para_t);
if (ret == IOCTL_ERROR)
{
printf("ov2640 can't wait event flag");
free(rgbbuffer);
return;
}
lcd_fill_array(0, 0, OV2640_X_RESOLUTION_IMAGE_OUTSIZE, OV2640_Y_RESOLUTION_IMAGE_OUTSIZE, rgbbuffer);
if (photo_times % 20 == 0)
{
resize_rgb565in_rgb888out(rgbbuffer, resized_buffer);
int pixel = 0;
for (int i = 0; i < 3 * width; i += 3 * width / CONV1_IN_DIM)
{
for (int j = 0; j < 3 * height; j += 3 * height / CONV1_IN_DIM)
{
for (int k = 0; k < 3; k++, pixel++)
{
*(in_buffer + pixel) = *(resized_buffer + 256 * i + j + k);
}
}
}
cmsisnn_inference_vegetable_classify(in_buffer);
}
photo_times++;
}
}
void cmsisnn_vegetable_classify()
{
fd = open("/dev/ov2640", O_RDONLY);
if (fd < 0)
{
printf("open ov2640 fail !!");
return;
}
printf("memory_init \n\r");
uint8_t *JpegBuffer = malloc(JPEG_BUF_SIZE);
if (JpegBuffer == NULL)
{
printf("JpegBuffer senddata buf malloc error!\n");
return;
}
resized_buffer = malloc(3 * width * height);
if (resized_buffer == NULL)
{
printf("Resized_buffer buf malloc error!\n");
return;
}
in_buffer = malloc(CONV1_IN_CH * CONV1_IN_DIM * CONV1_IN_DIM);
if (in_buffer == NULL)
{
printf("In_buffer buf malloc error!\n");
return;
}
memory_init();
printf("memory_init success\n\r");
shoot_para_t.pdata = (uint32_t)JpegBuffer;
shoot_para_t.length = JPEG_BUF_SIZE / 2;
int result = 0;
pthread_t tid = 0;
pthread_attr_t attr;
struct sched_param prio;
prio.sched_priority = 8;
size_t stack_size = 1024 * 11;
pthread_attr_init(&attr);
pthread_attr_setschedparam(&attr, &prio);
pthread_attr_setstacksize(&attr, stack_size);
result = pthread_create(&tid, &attr, lcd_show_ov2640_thread_detection, JpegBuffer);
if (0 == result) {
printf("thread_detect_entry successfully!\n");
} else {
printf("thread_detect_entry failed! error code is %d.\n", result);
close(fd);
}
return;
}
#ifdef __RT_THREAD_H__
MSH_CMD_EXPORT(cmsisnn_vegetable_classify, classify vegetable using cmsis-nn);
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

View File

@ -0,0 +1,108 @@
#include "nn_vegetable_classify.h"
static const q7_t conv1_w[CONV1_WT_SHAPE] = CONV1_WT;
static const q7_t conv1_b[CONV1_BIAS_SHAPE] = CONV1_BIAS;
static const q7_t conv2_w[CONV2_WT_SHAPE] = CONV2_WT;
static const q7_t conv2_b[CONV2_BIAS_SHAPE] = CONV2_BIAS;
// static const q7_t conv3_w[CONV3_WT_SHAPE] = CONV3_WT;
// static const q7_t conv3_b[CONV3_BIAS_SHAPE] = CONV3_BIAS;
static const q7_t interface_w[INTERFACE_WT_SHAPE] = INTERFACE_WT;
static const q7_t interface_b[INTERFACE_BIAS_SHAPE] = INTERFACE_BIAS;
static const q7_t linear_w[LINEAR_WT_SHAPE] = LINEAR_WT;
static const q7_t linear_b[LINEAR_BIAS_SHAPE] = LINEAR_BIAS;
q7_t *conv1_out = NULL;
q7_t *pool1_out = NULL;
q7_t *conv2_out = NULL;
q7_t *pool2_out = NULL;
// q7_t *conv3_out = NULL ;
q7_t *interface_out = NULL;
q7_t *linear_out = NULL;
q7_t *y_out = NULL;
q7_t *conv_buffer = NULL;
q7_t *fc_buffer = NULL;
void memory_init()
{
static int flag = 0;
if (flag == 0)
{
conv1_out = malloc(CONV1_OUT_CH * CONV1_OUT_DIM * CONV1_OUT_DIM);
if (conv1_out == NULL)
{
printf("conv1_out malloc failed...\n");
return;
}
pool1_out = malloc(CONV1_OUT_CH * POOL1_OUT_DIM * POOL1_OUT_DIM);
if (pool1_out == NULL)
{
printf("pool1_out malloc failed...\n");
return;
}
conv2_out = malloc(CONV2_OUT_CH * CONV2_OUT_DIM * CONV2_OUT_DIM);
if (conv2_out == NULL)
{
printf("conv2_out malloc failed...\n");
return;
}
pool2_out = malloc(CONV2_OUT_CH * POOL2_OUT_DIM * POOL2_OUT_DIM);
if (pool2_out == NULL)
{
printf("pool2_out malloc failed...\n");
return;
}
interface_out = malloc(INTERFACE_OUT_DIM);
if (interface_out == NULL)
{
printf("interface_out malloc failed...\n");
return;
}
linear_out = malloc(LINEAR_OUT_DIM);
if (linear_out == NULL)
{
printf("linear_out malloc failed...\n");
return;
}
y_out = malloc(LINEAR_OUT_DIM);
if (y_out == NULL)
{
printf("y_out malloc failed...\n");
return;
}
conv_buffer = malloc(MAX_CONV_BUFFER_SIZE);
if (conv_buffer == NULL)
{
printf("conv_buffer malloc failed...\n");
return;
}
fc_buffer = malloc(MAX_FC_BUFFER);
if (fc_buffer == NULL)
{
printf("fc_buffer malloc failed...\n");
return;
}
}
}
void run_nn_sn_classify(q7_t *input_data, q7_t *output_data)
{
for (int i = 0; i < CONV1_IN_CH * CONV1_IN_DIM * CONV1_IN_DIM; i++)
{
input_data[i] = input_data[i] - 127;
}
arm_convolve_HWC_q7_basic(input_data, CONV1_IN_DIM, CONV1_IN_CH, conv1_w, CONV1_OUT_CH, CONV1_KER_DIM, CONV1_PAD, CONV1_STRIDE, conv1_b, CONV1_BIAS_LSHIFT, CONV1_OUT_RSHIFT, conv1_out, CONV1_OUT_DIM, (q15_t *)conv_buffer, fc_buffer);
arm_maxpool_q7_HWC(conv1_out, POOL1_IN_DIM, POOL1_IN_CH, POOL1_KER_DIM, POOL1_PAD, POOL1_STRIDE, POOL1_OUT_DIM, NULL, pool1_out);
arm_relu_q7(pool1_out, POOL1_OUT_DIM * POOL1_OUT_DIM * CONV1_OUT_CH);
arm_convolve_HWC_q7_basic(pool1_out, CONV2_IN_DIM, CONV2_IN_CH, conv2_w, CONV2_OUT_CH, CONV2_KER_DIM, CONV2_PAD, CONV2_STRIDE, conv2_b, CONV2_BIAS_LSHIFT, CONV2_OUT_RSHIFT, conv2_out, CONV2_OUT_DIM, (q15_t *)conv_buffer, NULL);
arm_maxpool_q7_HWC(conv2_out, POOL2_IN_DIM, POOL2_IN_CH, POOL2_KER_DIM, POOL2_PAD, POOL2_STRIDE, POOL2_OUT_DIM, NULL, pool2_out);
arm_relu_q7(pool2_out, POOL2_OUT_DIM * POOL2_OUT_DIM * CONV2_OUT_CH);
// printf("1\n");
// arm_convolve_HWC_q7_basic(pool2_out, CONV3_IN_DIM, CONV3_IN_CH, conv3_w, CONV3_OUT_CH, CONV3_KER_DIM,
// CONV3_PAD, CONV3_STRIDE, conv3_b, CONV3_BIAS_LSHIFT, CONV3_OUT_RSHIFT, conv3_out,
// CONV3_OUT_DIM, (q15_t *) conv_buffer, NULL);
// arm_relu_q7(conv3_out, CONV3_OUT_DIM * CONV3_OUT_DIM * CONV3_OUT_CH);
// printf("2\n");
arm_fully_connected_q7_opt(pool2_out, interface_w, INTERFACE_IN_DIM, INTERFACE_OUT_DIM, INTERFACE_BIAS_LSHIFT, INTERFACE_OUT_RSHIFT, interface_b, interface_out, (q15_t *)fc_buffer);
arm_relu_q7(interface_out, INTERFACE_OUT_DIM);
arm_fully_connected_q7_opt(interface_out, linear_w, LINEAR_IN_DIM, LINEAR_OUT_DIM, LINEAR_BIAS_LSHIFT, LINEAR_OUT_RSHIFT, linear_b, output_data, (q15_t *)fc_buffer);
}

View File

@ -0,0 +1,13 @@
#ifndef __NN_H__
#define __NN_H__
#include <transform.h>
#include "arm_math.h"
#include "arm_nnfunctions.h"
#include "parameter_vegetable_classify.h"
#include "weights_vegetable_classify.h"
void run_nn_detection(q7_t* input_data, q7_t* output_data);
void memory_init();
#endif

View File

@ -0,0 +1,56 @@
#define CONV1_IN_CH 3
#define CONV1_OUT_CH 32
#define CONV1_KER_DIM 3
#define CONV1_PAD 0
#define CONV1_STRIDE 1
#define CONV1_IN_DIM 32
#define CONV1_OUT_DIM 30
#define MAX_CONV_BUFFER_SIZE 3096
#define POOL1_IN_CH 32
#define POOL1_KER_DIM 2
#define POOL1_PAD 0
#define POOL1_STRIDE 2
#define POOL1_IN_DIM 30
#define POOL1_OUT_DIM 15
#define CONV2_IN_CH 32
#define CONV2_OUT_CH 32
#define CONV2_KER_DIM 3
#define CONV2_PAD 0
#define CONV2_STRIDE 1
#define CONV2_IN_DIM 15
#define CONV2_OUT_DIM 13
#define POOL2_IN_CH 32
#define POOL2_KER_DIM 2
#define POOL2_PAD 0
#define POOL2_STRIDE 2
#define POOL2_IN_DIM 13
#define POOL2_OUT_DIM 6
#define INTERFACE_OUT_DIM 32
#define INTERFACE_IN_DIM 1152
#define MAX_FC_BUFFER 3096
#define LINEAR_OUT_DIM 4
#define LINEAR_IN_DIM 32
#define CONV1_BIAS_LSHIFT 6
#define CONV1_OUT_RSHIFT 9
#define CONV1_WEIGHT_Q 8
#define CONV1_BIAS_Q 8
#define CONV1_INPUT_Q 6
#define CONV1_OUT_Q 5
#define CONV2_BIAS_LSHIFT 4
#define CONV2_OUT_RSHIFT 10
#define CONV2_WEIGHT_Q 9
#define CONV2_BIAS_Q 10
#define CONV2_INPUT_Q 5
#define CONV2_OUT_Q 4
#define INTERFACE_BIAS_LSHIFT 3
#define INTERFACE_OUT_RSHIFT 11
#define INTERFACE_WEIGHT_Q 9
#define INTERFACE_BIAS_Q 10
#define INTERFACE_INPUT_Q 4
#define INTERFACE_OUT_Q 2
#define LINEAR_BIAS_LSHIFT 0
#define LINEAR_OUT_RSHIFT 7
#define LINEAR_WEIGHT_Q 7
#define LINEAR_BIAS_Q 9
#define LINEAR_INPUT_Q 2
#define LINEAR_OUT_Q 2

View File

@ -1,4 +1,5 @@
config IMAGE_PROCESSING_TJPGDEC_APP
bool "image processing apps/TJpgDec(example)"
select USING_IMAGE_PROCESSING
select IMAGE_PROCESSING_USING_TJPGD
default n

View File

@ -83,8 +83,7 @@ ifeq ($(CONFIG_USING_TENSORFLOWLITEMICRO),y)
-Isource/third_party/ruy
DEFINES += -DTF_LITE_USE_GLOBAL_CMATH_FUNCTIONS \
-DTF_LITE_USE_GLOBAL_MAX \
-DTF_LITE_USE_GLOBAL_MIN \
-DTF_LITE_STATIC_MEMORY
-DTF_LITE_USE_GLOBAL_MIN
endif
include $(KERNEL_ROOT)/compiler.mk

View File

@ -23,8 +23,8 @@ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(SRCS)))
LIBRARY_OBJS := $(filter-out tensorflow/lite/micro/examples/%, $(OBJS))
CXXFLAGS += -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy
CCFLAGS += -std=c11 -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy
CXXFLAGS += -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy
CCFLAGS += -std=c11 -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy
LDFLAGS += -lm

View File

@ -12,4 +12,5 @@ menu "lib"
source "$APP_DIR/lib/cJSON/Kconfig"
source "$APP_DIR/lib/queue/Kconfig"
source "$APP_DIR/lib/lvgl/Kconfig"
source "$APP_DIR/lib/embedded_database/Kconfig"
endmenu

View File

@ -0,0 +1,6 @@
menuconfig USING_EMBEDDED_DATABASE
bool "embedded database"
default n
if USING_EMBEDDED_DATABASE
source "$APP_DIR/lib/embedded_database/flashdb/Kconfig"
endif

View File

@ -0,0 +1,14 @@
import os
Import('RTT_ROOT')
from building import *
cwd = GetCurrentDir()
objs = []
list = os.listdir(cwd)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(path, 'SConscript'))
Return('objs')

View File

@ -0,0 +1,25 @@
menuconfig USING_EMBEDDED_DATABASE_FLASHDB
bool "flashdb an ultra-lightweight embedded database "
default n
if USING_EMBEDDED_DATABASE_FLASHDB
comment " fal io is not supported so far"
choice
prompt "select io mode"
default FDB_USING_FILE_POSIX_MODE
help
Select io mode
config FDB_USING_FILE_POSIX_MODE
bool "file posix io "
config FDB_USING_FILE_LIBC_MODE
bool "file libc io "
config FDB_USING_FAL_MODE
bool "flash fal io "
endchoice
config FDB_USING_KVDB
bool "Use key value database"
default n
config FDB_USING_TSDB
bool "Use time series database"
default n
endif

View File

@ -0,0 +1,10 @@
from building import *
import os
cwd = GetCurrentDir()
src = Glob('*.c')
group = DefineGroup('embedded database', src, depend = ['USING_EMBEDDED_DATABASE_FLASHDB'], CPPPATH = [cwd])
Return('group')

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Initialize interface.
*
* Some initialize interface for this library.
*/
#include <flashdb.h>
#include <fdb_low_lvl.h>
#include <string.h>
#define FDB_LOG_TAG ""
#if !defined(FDB_USING_FAL_MODE) && !defined(FDB_USING_FILE_MODE)
#error "Please defined the FDB_USING_FAL_MODE or FDB_USING_FILE_MODE macro"
#endif
fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_type type, void *user_data)
{
FDB_ASSERT(db);
FDB_ASSERT(name);
FDB_ASSERT(path);
if (db->init_ok) {
return FDB_NO_ERR;
}
db->name = name;
db->type = type;
db->user_data = user_data;
if (db->file_mode) {
#ifdef FDB_USING_FILE_MODE
/* must set when using file mode */
FDB_ASSERT(db->sec_size != 0);
FDB_ASSERT(db->max_size != 0);
#ifdef FDB_USING_FILE_POSIX_MODE
db->cur_file = -1;
#else
db->cur_file = 0;
#endif
db->storage.dir = path;
FDB_ASSERT(strlen(path) != 0)
#endif
} else {
#ifdef FDB_USING_FAL_MODE
size_t block_size;
/* FAL (Flash Abstraction Layer) initialization */
fal_init();
/* check the flash partition */
if ((db->storage.part = fal_partition_find(path)) == NULL) {
FDB_INFO("Error: Partition (%s) not found.\n", path);
return FDB_PART_NOT_FOUND;
}
block_size = fal_flash_device_find(db->storage.part->flash_name)->blk_size;
if (db->sec_size == 0) {
db->sec_size = block_size;
} else {
/* must be aligned with block size */
FDB_ASSERT(db->sec_size % block_size == 0);
}
db->max_size = db->storage.part->len;
#endif /* FDB_USING_FAL_MODE */
}
/* must align with sector size */
FDB_ASSERT(db->max_size % db->sec_size == 0);
/* must have more than or equal 2 sector */
FDB_ASSERT(db->max_size / db->sec_size >= 2);
return FDB_NO_ERR;
}
void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
{
static bool log_is_show = false;
if (result == FDB_NO_ERR) {
db->init_ok = true;
if (!log_is_show) {
FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION);
FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
log_is_show = true;
}
} else if (!db->not_formatable) {
FDB_INFO("Error: %s (%s) is initialize fail (%d).\n", db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB",
db->name, (int)result);
}
}
void _fdb_deinit(fdb_db_t db)
{
FDB_ASSERT(db);
if (db->init_ok) {
#ifdef FDB_USING_FILE_MODE
#ifdef FDB_USING_FILE_POSIX_MODE
if (db->cur_file > 0) {
#if !defined(_MSC_VER)
#include <unistd.h>
#endif
close(db->cur_file);
}
#else
if (db->cur_file != 0) {
fclose(db->cur_file);
}
#endif /* FDB_USING_FILE_POSIX_MODE */
#endif /* FDB_USING_FILE_MODE */
}
db->init_ok = false;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief configuration file
*/
#ifndef _FDB_CFG_H_
#define _FDB_CFG_H_
#ifdef FDB_USING_FAL_MODE
/* the flash write granularity, unit: bit
* only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1) */
#define FDB_WRITE_GRAN /* @note you must define it for a value */
#endif
/* Using file storage mode by LIBC file API, like fopen/fread/fwrte/fclose */
/* #define FDB_USING_FILE_LIBC_MODE */
/* Using file storage mode by POSIX file API, like open/read/write/close */
/* #define FDB_USING_FILE_POSIX_MODE */
/* MCU Endian Configuration, default is Little Endian Order. */
/* #define FDB_BIG_ENDIAN */
/* log print macro. default EF_PRINT macro is printf() */
/* #define FDB_PRINT(...) my_printf(__VA_ARGS__) */
/* print debug information */
#define FDB_DEBUG_ENABLE
#endif /* _FDB_CFG_H_ */

View File

@ -0,0 +1,340 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public definition.
*/
#ifndef _FDB_DEF_H_
#define _FDB_DEF_H_
#ifdef __cplusplus
extern "C" {
#endif
/* software version number */
#define FDB_SW_VERSION "1.1.0"
#define FDB_SW_VERSION_NUM 0x10100
/* the KV max name length must less then it */
#ifndef FDB_KV_NAME_MAX
#define FDB_KV_NAME_MAX 64
#endif
/* the KV cache table size, it will improve KV search speed when using cache */
#ifndef FDB_KV_CACHE_TABLE_SIZE
#define FDB_KV_CACHE_TABLE_SIZE 64
#endif
/* the sector cache table size, it will improve KV save speed when using cache */
#ifndef FDB_SECTOR_CACHE_TABLE_SIZE
#define FDB_SECTOR_CACHE_TABLE_SIZE 4
#endif
#if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0)
#define FDB_KV_USING_CACHE
#endif
#if defined(FDB_USING_FILE_LIBC_MODE) || defined(FDB_USING_FILE_POSIX_MODE)
#define FDB_USING_FILE_MODE
#endif
#ifndef FDB_WRITE_GRAN
#define FDB_WRITE_GRAN 1
#endif
/* log function. default FDB_PRINT macro is printf() */
#ifndef FDB_PRINT
#define FDB_PRINT(...) printf(__VA_ARGS__)
#endif
#define FDB_LOG_PREFIX1() FDB_PRINT("[FlashDB]" FDB_LOG_TAG)
#define FDB_LOG_PREFIX2() FDB_PRINT(" ")
#define FDB_LOG_PREFIX() FDB_LOG_PREFIX1();FDB_LOG_PREFIX2()
#ifdef FDB_DEBUG_ENABLE
#define FDB_DEBUG(...) FDB_LOG_PREFIX();FDB_PRINT("(%s:%d) ", __FILE__, __LINE__);FDB_PRINT(__VA_ARGS__)
#else
#define FDB_DEBUG(...)
#endif
/* routine print function. Must be implement by user. */
#define FDB_INFO(...) FDB_LOG_PREFIX();FDB_PRINT(__VA_ARGS__)
/* assert for developer. */
#define FDB_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
FDB_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
while (1); \
}
#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x00 /**< set sector size control command, this change MUST before database initialization */
#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x01 /**< get sector size control command */
#define FDB_KVDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */
#define FDB_KVDB_CTRL_SET_UNLOCK 0x03 /**< set unlock function control command */
#define FDB_KVDB_CTRL_SET_FILE_MODE 0x09 /**< set file mode control command, this change MUST before database initialization */
#define FDB_KVDB_CTRL_SET_MAX_SIZE 0x0A /**< set database max size in file mode control command, this change MUST before database initialization */
#define FDB_KVDB_CTRL_SET_NOT_FORMAT 0x0B /**< set database NOT format mode control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x00 /**< set sector size control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x01 /**< get sector size control command */
#define FDB_TSDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */
#define FDB_TSDB_CTRL_SET_UNLOCK 0x03 /**< set unlock function control command */
#define FDB_TSDB_CTRL_SET_ROLLOVER 0x04 /**< set rollover control command, this change MUST after database initialization */
#define FDB_TSDB_CTRL_GET_ROLLOVER 0x05 /**< get rollover control command */
#define FDB_TSDB_CTRL_GET_LAST_TIME 0x06 /**< get last save time control command */
#define FDB_TSDB_CTRL_SET_FILE_MODE 0x09 /**< set file mode control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_SET_MAX_SIZE 0x0A /**< set database max size in file mode control command, this change MUST before database initialization */
#define FDB_TSDB_CTRL_SET_NOT_FORMAT 0x0B /**< set database NOT formatable mode control command, this change MUST before database initialization */
#ifdef FDB_USING_TIMESTAMP_64BIT
typedef int64_t fdb_time_t;
#else
typedef int32_t fdb_time_t;
#endif /* FDB_USING_TIMESTAMP_64BIT */
typedef fdb_time_t (*fdb_get_time)(void);
struct fdb_default_kv_node {
char *key;
void *value;
size_t value_len;
};
struct fdb_default_kv {
struct fdb_default_kv_node *kvs;
size_t num;
};
/* error code */
typedef enum {
FDB_NO_ERR,
FDB_ERASE_ERR,
FDB_READ_ERR,
FDB_WRITE_ERR,
FDB_PART_NOT_FOUND,
FDB_KV_NAME_ERR,
FDB_KV_NAME_EXIST,
FDB_SAVED_FULL,
FDB_INIT_FAILED,
} fdb_err_t;
enum fdb_kv_status {
FDB_KV_UNUSED,
FDB_KV_PRE_WRITE,
FDB_KV_WRITE,
FDB_KV_PRE_DELETE,
FDB_KV_DELETED,
FDB_KV_ERR_HDR,
#define FDB_KV_STATUS_NUM 6
};
typedef enum fdb_kv_status fdb_kv_status_t;
enum fdb_tsl_status {
FDB_TSL_UNUSED,
FDB_TSL_PRE_WRITE,
FDB_TSL_WRITE,
FDB_TSL_USER_STATUS1,
FDB_TSL_DELETED,
FDB_TSL_USER_STATUS2,
#define FDB_TSL_STATUS_NUM 6
};
typedef enum fdb_tsl_status fdb_tsl_status_t;
/* key-value node object */
struct fdb_kv {
fdb_kv_status_t status; /**< node status, @see fdb_kv_status_t */
bool crc_is_ok; /**< node CRC32 check is OK */
uint8_t name_len; /**< name length */
uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */
uint32_t len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
uint32_t value_len; /**< value length */
char name[FDB_KV_NAME_MAX]; /**< name */
struct {
uint32_t start; /**< node start address */
uint32_t value; /**< value start address */
} addr;
};
typedef struct fdb_kv *fdb_kv_t;
struct fdb_kv_iterator {
struct fdb_kv curr_kv; /**< Current KV we get from the iterator */
uint32_t iterated_cnt; /**< How many KVs have we iterated already */
size_t iterated_obj_bytes; /**< Total storage size of KVs we have iterated. */
size_t iterated_value_bytes; /**< Total value size of KVs we have iterated. */
uint32_t sector_addr; /**< Current sector address we're iterating. DO NOT touch it. */
};
typedef struct fdb_kv_iterator *fdb_kv_iterator_t;
/* time series log node object */
struct fdb_tsl {
fdb_tsl_status_t status; /**< node status, @see fdb_log_status_t */
fdb_time_t time; /**< node timestamp */
uint32_t log_len; /**< log length, must align by FDB_WRITE_GRAN */
struct {
uint32_t index; /**< node index address */
uint32_t log; /**< log data address */
} addr;
};
typedef struct fdb_tsl *fdb_tsl_t;
typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg);
typedef enum {
FDB_DB_TYPE_KV,
FDB_DB_TYPE_TS,
} fdb_db_type;
/* the flash sector store status */
enum fdb_sector_store_status {
FDB_SECTOR_STORE_UNUSED,
FDB_SECTOR_STORE_EMPTY,
FDB_SECTOR_STORE_USING,
FDB_SECTOR_STORE_FULL,
#define FDB_SECTOR_STORE_STATUS_NUM 4
};
typedef enum fdb_sector_store_status fdb_sector_store_status_t;
/* the flash sector dirty status */
enum fdb_sector_dirty_status {
FDB_SECTOR_DIRTY_UNUSED,
FDB_SECTOR_DIRTY_FALSE,
FDB_SECTOR_DIRTY_TRUE,
FDB_SECTOR_DIRTY_GC,
#define FDB_SECTOR_DIRTY_STATUS_NUM 4
};
typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
/* KVDB section information */
struct kvdb_sec_info {
bool check_ok; /**< sector header check is OK */
struct {
fdb_sector_store_status_t store; /**< sector store status @see fdb_sector_store_status_t */
fdb_sector_dirty_status_t dirty; /**< sector dirty status @see sector_dirty_status_t */
} status;
uint32_t addr; /**< sector start address */
uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */
uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */
size_t remain; /**< remain size */
uint32_t empty_kv; /**< the next empty KV node start address */
};
typedef struct kvdb_sec_info *kv_sec_info_t;
/* TSDB section information */
struct tsdb_sec_info {
bool check_ok; /**< sector header check is OK */
fdb_sector_store_status_t status; /**< sector store status @see fdb_sector_store_status_t */
uint32_t addr; /**< sector start address */
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
fdb_time_t start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */
fdb_time_t end_time; /**< the last end node's timestamp, 0xFFFFFFFF: unused */
uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */
fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */
size_t remain; /**< remain size */
uint32_t empty_idx; /**< the next empty node index address */
uint32_t empty_data; /**< the next empty node's data end address */
};
typedef struct tsdb_sec_info *tsdb_sec_info_t;
struct kv_cache_node {
uint16_t name_crc; /**< KV name's CRC32 low 16bit value */
uint16_t active; /**< KV node access active degree */
uint32_t addr; /**< KV node address */
};
typedef struct kv_cache_node *kv_cache_node_t;
struct sector_cache_node {
uint32_t addr; /**< sector start address */
uint32_t empty_addr; /**< sector empty address */
};
typedef struct sector_cache_node *sector_cache_node_t;
/* database structure */
typedef struct fdb_db *fdb_db_t;
struct fdb_db {
const char *name; /**< database name */
fdb_db_type type; /**< database type */
union {
#ifdef FDB_USING_FAL_MODE
const struct fal_partition *part; /**< flash partition for saving database */
#endif
#ifdef FDB_USING_FILE_MODE
const char *dir; /**< directory path for saving database */
#endif
} storage;
uint32_t sec_size; /**< flash section size. It's a multiple of block size */
uint32_t max_size; /**< database max size. It's a multiple of section size */
bool init_ok; /**< initialized successfully */
bool file_mode; /**< is file mode, default is false */
bool not_formatable; /**< is can NOT be formated mode, default is false */
#ifdef FDB_USING_FILE_MODE
#if defined(FDB_USING_FILE_POSIX_MODE)
int cur_file; /**< current file object */
#elif defined(FDB_USING_FILE_LIBC_MODE)
FILE *cur_file; /**< current file object */
#endif
uint32_t cur_sec; /**< current operate sector address */
#endif
void (*lock)(fdb_db_t db); /**< lock the database operate */
void (*unlock)(fdb_db_t db); /**< unlock the database operate */
void *user_data;
};
/* KVDB structure */
struct fdb_kvdb {
struct fdb_db parent; /**< inherit from fdb_db */
struct fdb_default_kv default_kvs; /**< default KV */
bool gc_request; /**< request a GC check */
bool in_recovery_check; /**< is in recovery check status when first reboot */
struct fdb_kv cur_kv;
struct kvdb_sec_info cur_sector;
bool last_is_complete_del;
#ifdef FDB_KV_USING_CACHE
/* KV cache table */
struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
/* sector cache table, it caching the sector info which status is current using */
struct sector_cache_node sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
#endif /* FDB_KV_USING_CACHE */
#ifdef FDB_KV_AUTO_UPDATE
uint32_t ver_num; /**< setting version number for update */
#endif
void *user_data;
};
typedef struct fdb_kvdb *fdb_kvdb_t;
/* TSDB structure */
struct fdb_tsdb {
struct fdb_db parent; /**< inherit from fdb_db */
struct tsdb_sec_info cur_sec; /**< current using sector */
fdb_time_t last_time; /**< last TSL timestamp */
fdb_get_time get_time; /**< the current timestamp get function */
size_t max_len; /**< the maximum length of each log */
uint32_t oldest_addr; /**< the oldest sector start address */
bool rollover; /**< the oldest data will rollover by newest data, default is true */
void *user_data;
};
typedef struct fdb_tsdb *fdb_tsdb_t;
/* blob structure */
struct fdb_blob {
void *buf; /**< blob data buffer */
size_t size; /**< blob data buffer size */
struct {
uint32_t meta_addr; /**< saved KV or TSL index address */
uint32_t addr; /**< blob data saved address */
size_t len; /**< blob data saved length */
} saved;
};
typedef struct fdb_blob *fdb_blob_t;
#ifdef __cplusplus
}
#endif
#endif /* _FDB_DEF_H_ */

View File

@ -0,0 +1,221 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
* Copyright (c) 2020, enkiller, <462747508@qq.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <flashdb.h>
#include <fdb_low_lvl.h>
#define FDB_LOG_TAG "[file]"
#ifdef FDB_USING_FILE_MODE
#define DB_PATH_MAX 256
static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size)
{
#define DB_NAME_MAX 8
/* from db_name.fdb.0 to db_name.fdb.n */
char file_name[DB_NAME_MAX + 4 + 10];
uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
int index = sec_addr / db->sec_size;
snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name, index);
if (strlen(db->storage.dir) + 1 + strlen(file_name) >= size) {
/* path is too long */
FDB_ASSERT(0)
}
snprintf(path, size, "%s/%s", db->storage.dir, file_name);
}
#if defined(FDB_USING_FILE_POSIX_MODE)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if !defined(_MSC_VER)
#include <unistd.h>
#endif
static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
{
uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
int fd = db->cur_file;
char path[DB_PATH_MAX];
if (sec_addr != db->cur_sec || fd <= 0 || clean) {
get_db_file_path(db, addr, path, DB_PATH_MAX);
if (fd > 0) {
close(fd);
fd = -1;
}
if (clean) {
/* clean the old file */
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0777);
if (fd <= 0) {
FDB_INFO("Error: open (%s) file failed.\n", path);
}
else {
close(fd);
fd = -1;
}
}
/* open the database file */
fd = open(path, O_RDWR, 0777);
db->cur_sec = sec_addr;
}
db->cur_file = fd;
return db->cur_file;
}
fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
int fd = open_db_file(db, addr, false);
if (fd > 0) {
addr = addr % db->sec_size;
lseek(fd, addr, SEEK_SET);
read(fd, buf, size);
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
{
fdb_err_t result = FDB_NO_ERR;
int fd = open_db_file(db, addr, false);
if (fd > 0) {
addr = addr % db->sec_size;
lseek(fd, addr, SEEK_SET);
write(fd, buf, size);
if(sync) {
fsync(fd);
}
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
int fd = open_db_file(db, addr, true);
if (fd > 0) {
#define BUF_SIZE 32
uint8_t buf[BUF_SIZE];
size_t i;
lseek(fd, 0, SEEK_SET);
for (i = 0; i * BUF_SIZE < size; i++)
{
memset(buf, 0xFF, BUF_SIZE);
write(fd, buf, BUF_SIZE);
}
memset(buf, 0xFF, BUF_SIZE);
write(fd, buf, size - i * BUF_SIZE);
fsync(fd);
} else {
result = FDB_ERASE_ERR;
}
return result;
}
#elif defined(FDB_USING_FILE_LIBC_MODE)
static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean)
{
uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
if (sec_addr != db->cur_sec || db->cur_file == NULL || clean) {
char path[DB_PATH_MAX];
get_db_file_path(db, addr, path, DB_PATH_MAX);
if (db->cur_file) {
fclose(db->cur_file);
}
if (clean) {
/* clean the old file */
db->cur_file = fopen(path, "wb+");
if (db->cur_file == NULL) {
FDB_INFO("Error: open (%s) file failed.\n", path);
} else {
fclose(db->cur_file);
}
}
/* open the database file */
db->cur_file = fopen(path, "rb+");
db->cur_sec = sec_addr;
}
return db->cur_file;
}
fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
FILE *fp = open_db_file(db, addr, false);
if (fp) {
addr = addr % db->sec_size;
fseek(fp, addr, SEEK_SET);
fread(buf, size, 1, fp);
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
{
fdb_err_t result = FDB_NO_ERR;
FILE *fp = open_db_file(db, addr, false);
if (fp) {
addr = addr % db->sec_size;
fseek(fp, addr, SEEK_SET);
fwrite(buf, size, 1, fp);
if(sync) {
fflush(fp);
}
} else {
result = FDB_READ_ERR;
}
return result;
}
fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
FILE *fp = open_db_file(db, addr, true);
if (fp != NULL) {
#define BUF_SIZE 32
uint8_t buf[BUF_SIZE];
size_t i;
fseek(fp, 0, SEEK_SET);
for (i = 0; i * BUF_SIZE < size; i++)
{
memset(buf, 0xFF, BUF_SIZE);
fwrite(buf, BUF_SIZE, 1, fp);
}
memset(buf, 0xFF, BUF_SIZE);
fwrite(buf, size - i * BUF_SIZE, 1, fp);
fflush(fp);
} else {
result = FDB_ERASE_ERR;
}
return result;
}
#endif /* defined(FDB_USING_FILE_LIBC_MODE) */
#endif /* FDB_USING_FILE_MODE */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief low level API and definition
*/
#ifndef _FDB_LOW_LVL_H_
#define _FDB_LOW_LVL_H_
#include <fdb_cfg.h>
#include <fdb_def.h>
#if (FDB_WRITE_GRAN == 1)
#define FDB_STATUS_TABLE_SIZE(status_number) ((status_number * FDB_WRITE_GRAN + 7)/8)
#else
#define FDB_STATUS_TABLE_SIZE(status_number) (((status_number - 1) * FDB_WRITE_GRAN + 7)/8)
#endif
/* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4)
* would return 16.
*/
#define FDB_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
/* align by write granularity */
#define FDB_WG_ALIGN(size) (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7)/8))
/**
* Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
* would return 12.
*/
#define FDB_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
/* align down by write granularity */
#define FDB_WG_ALIGN_DOWN(size) (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7)/8))
#define FDB_STORE_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM)
#define FDB_DIRTY_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM)
/* the data is unused */
#define FDB_DATA_UNUSED 0xFFFFFFFF
fdb_err_t _fdb_kv_load(fdb_kvdb_t db);
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index);
size_t _fdb_get_status(uint8_t status_table[], size_t status_num);
uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end);
fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data);
void _fdb_init_finish(fdb_db_t db, fdb_err_t result);
void _fdb_deinit(fdb_db_t db);
fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync);
size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num);
fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size);
fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
#endif /* _FDB_LOW_LVL_H_ */

View File

@ -0,0 +1,831 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief TSDB feature.
*
* Time series log (like TSDB) feature implement source file.
*
* TSL is time series log, the TSDB saved many TSLs.
*/
#include <inttypes.h>
#include <string.h>
#include <flashdb.h>
#include <fdb_low_lvl.h>
#define FDB_LOG_TAG "[tsl]"
/* rewrite log prefix */
#undef FDB_LOG_PREFIX2
#define FDB_LOG_PREFIX2() FDB_PRINT("[%s] ", db_name(db))
#if defined(FDB_USING_TSDB)
/* magic word(`T`, `S`, `L`, `0`) */
#define SECTOR_MAGIC_WORD 0x304C5354
#define TSL_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_TSL_STATUS_NUM)
#define SECTOR_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct sector_hdr_data)))
#define LOG_IDX_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct log_idx_data)))
#define LOG_IDX_TS_OFFSET ((unsigned long)(&((struct log_idx_data *)0)->time))
#define SECTOR_MAGIC_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->magic))
#define SECTOR_START_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->start_time))
#define SECTOR_END0_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].time))
#define SECTOR_END0_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].index))
#define SECTOR_END0_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].status))
#define SECTOR_END1_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].time))
#define SECTOR_END1_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].index))
#define SECTOR_END1_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].status))
/* the next address is get failed */
#define FAILED_ADDR 0xFFFFFFFF
#define db_name(db) (((fdb_db_t)db)->name)
#define db_init_ok(db) (((fdb_db_t)db)->init_ok)
#define db_sec_size(db) (((fdb_db_t)db)->sec_size)
#define db_max_size(db) (((fdb_db_t)db)->max_size)
#define db_lock(db) \
do { \
if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db); \
} while(0);
#define db_unlock(db) \
do { \
if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db); \
} while(0);
#define _FDB_WRITE_STATUS(db, addr, status_table, status_num, status_index, sync) \
do { \
result = _fdb_write_status((fdb_db_t)db, addr, status_table, status_num, status_index, sync);\
if (result != FDB_NO_ERR) return result; \
} while(0);
#define FLASH_WRITE(db, addr, buf, size, sync) \
do { \
result = _fdb_flash_write((fdb_db_t)db, addr, buf, size, sync); \
if (result != FDB_NO_ERR) return result; \
} while(0);
struct sector_hdr_data {
uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
fdb_time_t start_time; /**< the first start node's timestamp */
struct {
fdb_time_t time; /**< the last end node's timestamp */
uint32_t index; /**< the last end node's index */
uint8_t status[TSL_STATUS_TABLE_SIZE]; /**< end node status, @see fdb_tsl_status_t */
} end_info[2];
uint32_t reserved;
};
typedef struct sector_hdr_data *sector_hdr_data_t;
/* time series log node index data */
struct log_idx_data {
uint8_t status_table[TSL_STATUS_TABLE_SIZE]; /**< node status, @see fdb_tsl_status_t */
fdb_time_t time; /**< node timestamp */
uint32_t log_len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
uint32_t log_addr; /**< node address */
};
typedef struct log_idx_data *log_idx_data_t;
struct query_count_args {
fdb_tsl_status_t status;
size_t count;
};
struct check_sec_hdr_cb_args {
fdb_tsdb_t db;
bool check_failed;
size_t empty_num;
uint32_t empty_addr;
};
static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl)
{
struct log_idx_data idx;
/* read TSL index raw data */
_fdb_flash_read((fdb_db_t)db, tsl->addr.index, (uint32_t *) &idx, sizeof(struct log_idx_data));
tsl->status = (fdb_tsl_status_t) _fdb_get_status(idx.status_table, FDB_TSL_STATUS_NUM);
if (tsl->status == FDB_TSL_PRE_WRITE) {
tsl->log_len = db->max_len;
tsl->addr.log = FDB_DATA_UNUSED;
tsl->time = 0;
} else {
tsl->log_len = idx.log_len;
tsl->addr.log = idx.log_addr;
tsl->time = idx.time;
}
return FDB_NO_ERR;
}
static uint32_t get_next_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len)
{
if (traversed_len + db_sec_size(db) <= db_max_size(db)) {
if (pre_sec->addr + db_sec_size(db) < db_max_size(db)) {
return pre_sec->addr + db_sec_size(db);
} else {
/* the next sector is on the top of the database */
return 0;
}
} else {
/* finished */
return FAILED_ADDR;
}
}
static uint32_t get_next_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl)
{
uint32_t addr = FAILED_ADDR;
if (sector->status == FDB_SECTOR_STORE_EMPTY) {
return FAILED_ADDR;
}
if (pre_tsl->addr.index + LOG_IDX_DATA_SIZE <= sector->end_idx) {
addr = pre_tsl->addr.index + LOG_IDX_DATA_SIZE;
} else {
/* no TSL */
return FAILED_ADDR;
}
return addr;
}
static fdb_err_t read_sector_info(fdb_tsdb_t db, uint32_t addr, tsdb_sec_info_t sector, bool traversal)
{
fdb_err_t result = FDB_NO_ERR;
struct sector_hdr_data sec_hdr;
FDB_ASSERT(sector);
/* read sector header raw data */
_fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data));
sector->addr = addr;
sector->magic = sec_hdr.magic;
/* check magic word */
if (sector->magic != SECTOR_MAGIC_WORD) {
sector->check_ok = false;
return FDB_INIT_FAILED;
}
sector->check_ok = true;
sector->status = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM);
sector->start_time = sec_hdr.start_time;
sector->end_info_stat[0] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[0].status, FDB_TSL_STATUS_NUM);
sector->end_info_stat[1] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[1].status, FDB_TSL_STATUS_NUM);
if (sector->end_info_stat[0] == FDB_TSL_WRITE) {
sector->end_time = sec_hdr.end_info[0].time;
sector->end_idx = sec_hdr.end_info[0].index;
} else if (sector->end_info_stat[1] == FDB_TSL_WRITE) {
sector->end_time = sec_hdr.end_info[1].time;
sector->end_idx = sec_hdr.end_info[1].index;
} else if (sector->end_info_stat[0] == FDB_TSL_PRE_WRITE && sector->end_info_stat[1] == FDB_TSL_PRE_WRITE) {
//TODO There is no valid end node info on this sector, need impl fast query this sector by fdb_tsl_iter_by_time
FDB_ASSERT(0);
}
/* traversal all TSL and calculate the remain space size */
sector->empty_idx = sector->addr + SECTOR_HDR_DATA_SIZE;
sector->empty_data = sector->addr + db_sec_size(db);
/* the TSL's data is saved from sector bottom, and the TSL's index saved from the sector top */
sector->remain = sector->empty_data - sector->empty_idx;
if (sector->status == FDB_SECTOR_STORE_USING && traversal) {
struct fdb_tsl tsl;
tsl.addr.index = sector->empty_idx;
while (read_tsl(db, &tsl) == FDB_NO_ERR) {
if (tsl.status == FDB_TSL_UNUSED) {
break;
}
sector->end_time = tsl.time;
sector->end_idx = tsl.addr.index;
sector->empty_idx += LOG_IDX_DATA_SIZE;
sector->empty_data -= FDB_WG_ALIGN(tsl.log_len);
tsl.addr.index += LOG_IDX_DATA_SIZE;
if (sector->remain > LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len)) {
sector->remain -= (LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len));
} else {
FDB_INFO("Error: this TSL (0x%08" PRIX32 ") size (%" PRIu32 ") is out of bound.\n", tsl.addr.index, tsl.log_len);
sector->remain = 0;
result = FDB_READ_ERR;
break;
}
}
}
return result;
}
static fdb_err_t format_sector(fdb_tsdb_t db, uint32_t addr)
{
fdb_err_t result = FDB_NO_ERR;
struct sector_hdr_data sec_hdr;
FDB_ASSERT(addr % db_sec_size(db) == 0);
result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db));
if (result == FDB_NO_ERR) {
_FDB_WRITE_STATUS(db, addr, sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY, true);
/* set the magic */
sec_hdr.magic = SECTOR_MAGIC_WORD;
FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic), true);
}
return result;
}
static void sector_iterator(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_sector_store_status_t status, void *arg1,
void *arg2, bool (*callback)(tsdb_sec_info_t sector, void *arg1, void *arg2), bool traversal)
{
uint32_t sec_addr = sector->addr, traversed_len = 0;
/* search all sectors */
do {
read_sector_info(db, sec_addr, sector, false);
if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status) {
if (traversal) {
read_sector_info(db, sec_addr, sector, true);
}
/* iterator is interrupted when callback return true */
if (callback && callback(sector, arg1, arg2)) {
return;
}
}
traversed_len += db_sec_size(db);
} while ((sec_addr = get_next_sector_addr(db, sector, traversed_len)) != FAILED_ADDR);
}
static fdb_err_t write_tsl(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t time)
{
fdb_err_t result = FDB_NO_ERR;
struct log_idx_data idx;
uint32_t idx_addr = db->cur_sec.empty_idx;
idx.log_len = blob->size;
idx.time = time;
idx.log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(idx.log_len);
/* write the status will by write granularity */
_FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
/* write other index info */
FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.time, sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET, false);
/* write blob data */
FLASH_WRITE(db, idx.log_addr, blob->buf, blob->size, false);
/* write the status will by write granularity */
_FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
return result;
}
static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_blob_t blob, fdb_time_t cur_time)
{
fdb_err_t result = FDB_NO_ERR;
uint8_t status[FDB_STORE_STATUS_TABLE_SIZE];
if (sector->status == FDB_SECTOR_STORE_USING && sector->remain < LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size)) {
uint8_t end_status[TSL_STATUS_TABLE_SIZE];
uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr;
/* save the end node index and timestamp */
if (sector->end_info_stat[0] == FDB_TSL_UNUSED) {
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index), false);
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
} else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) {
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index), false);
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
}
/* change current sector to full */
_FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true);
sector->status = FDB_SECTOR_STORE_FULL;
/* calculate next sector address */
if (sector->addr + db_sec_size(db) < db_max_size(db)) {
new_sec_addr = sector->addr + db_sec_size(db);
}
else if (db->rollover) {
new_sec_addr = 0;
} else {
/* not rollover */
return FDB_SAVED_FULL;
}
read_sector_info(db, new_sec_addr, &db->cur_sec, false);
if (sector->status != FDB_SECTOR_STORE_EMPTY) {
/* calculate the oldest sector address */
if (new_sec_addr + db_sec_size(db) < db_max_size(db)) {
db->oldest_addr = new_sec_addr + db_sec_size(db);
} else {
db->oldest_addr = 0;
}
format_sector(db, new_sec_addr);
read_sector_info(db, new_sec_addr, &db->cur_sec, false);
}
} else if (sector->status == FDB_SECTOR_STORE_FULL) {
/* database full */
return FDB_SAVED_FULL;
}
if (sector->status == FDB_SECTOR_STORE_EMPTY) {
/* change the sector to using */
sector->status = FDB_SECTOR_STORE_USING;
sector->start_time = cur_time;
_FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true);
/* save the start timestamp */
FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t), true);
}
return result;
}
static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
{
fdb_err_t result = FDB_NO_ERR;
fdb_time_t cur_time = db->get_time();
FDB_ASSERT(blob->size <= db->max_len);
/* check the current timestamp, MUST more than the last save timestamp */
if (cur_time < db->last_time) {
FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\n",
(intmax_t )cur_time, (intmax_t )(db->last_time));
return FDB_WRITE_ERR;
}
result = update_sec_status(db, &db->cur_sec, blob, cur_time);
if (result != FDB_NO_ERR) {
return result;
}
/* write the TSL node */
result = write_tsl(db, blob, cur_time);
if (result != FDB_NO_ERR) {
return result;
}
/* recalculate the current using sector info */
db->cur_sec.end_idx = db->cur_sec.empty_idx;
db->cur_sec.end_time = cur_time;
db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE;
db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size);
db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size);
db->last_time = cur_time;
return result;
}
/**
* Append a new log to TSDB.
*
* @param db database object
* @param blob log blob data
*
* @return result
*/
fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
{
fdb_err_t result = FDB_NO_ERR;
if (!db_init_ok(db)) {
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
return FDB_INIT_FAILED;
}
db_lock(db);
result = tsl_append(db, blob);
db_unlock(db);
return result;
}
/**
* The TSDB iterator for each TSL.
*
* @param db database object
* @param cb callback
* @param arg callback argument
*/
void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *arg)
{
struct tsdb_sec_info sector;
uint32_t sec_addr, traversed_len = 0;
struct fdb_tsl tsl;
if (!db_init_ok(db)) {
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
}
if (cb == NULL) {
return;
}
sec_addr = db->oldest_addr;
/* search all sectors */
do {
traversed_len += db_sec_size(db);
if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
continue;
}
/* sector has TSL */
if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) {
if (sector.status == FDB_SECTOR_STORE_USING) {
/* copy the current using sector status */
sector = db->cur_sec;
}
tsl.addr.index = sector.addr + SECTOR_HDR_DATA_SIZE;
/* search all TSL */
do {
read_tsl(db, &tsl);
/* iterator is interrupted when callback return true */
if (cb(&tsl, arg)) {
return;
}
} while ((tsl.addr.index = get_next_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
}
} while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
}
/**
* The TSDB iterator for each TSL by timestamp.
*
* @param db database object
* @param from starting timestap
* @param to ending timestap
* @param cb callback
* @param arg callback argument
*/
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)
{
struct tsdb_sec_info sector;
uint32_t sec_addr, oldest_addr = db->oldest_addr, traversed_len = 0;
struct fdb_tsl tsl;
bool found_start_tsl = false;
if (!db_init_ok(db)) {
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
}
// FDB_INFO("from %s", ctime((const time_t * )&from));
// FDB_INFO("to %s", ctime((const time_t * )&to));
if (cb == NULL) {
return;
}
sec_addr = oldest_addr;
/* search all sectors */
do {
traversed_len += db_sec_size(db);
if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
continue;
}
/* sector has TSL */
if ((sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL)) {
if (sector.status == FDB_SECTOR_STORE_USING) {
/* copy the current using sector status */
sector = db->cur_sec;
}
if ((found_start_tsl) || (!found_start_tsl && ((from >= sector.start_time && from <= sector.end_time)
|| (sec_addr == oldest_addr && from <= sector.start_time)))) {
uint32_t start = sector.addr + SECTOR_HDR_DATA_SIZE, end = sector.end_idx;
found_start_tsl = true;
/* search start TSL address, using binary search algorithm */
while (start <= end) {
tsl.addr.index = start + ((end - start) / 2 + 1) / LOG_IDX_DATA_SIZE * LOG_IDX_DATA_SIZE;
read_tsl(db, &tsl);
if (tsl.time < from) {
start = tsl.addr.index + LOG_IDX_DATA_SIZE;
} else {
end = tsl.addr.index - LOG_IDX_DATA_SIZE;
}
}
tsl.addr.index = start;
/* search all TSL */
do {
read_tsl(db, &tsl);
if (tsl.time >= from && tsl.time <= to) {
/* iterator is interrupted when callback return true */
if (cb(&tsl, cb_arg)) {
return;
}
} else {
return;
}
} while ((tsl.addr.index = get_next_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
}
} else if (sector.status == FDB_SECTOR_STORE_EMPTY) {
return;
}
} while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
}
static bool query_count_cb(fdb_tsl_t tsl, void *arg)
{
struct query_count_args *args = arg;
if (tsl->status == args->status) {
args->count++;
}
return false;
}
/**
* Query some TSL's count by timestamp and status.
*
* @param db database object
* @param from starting timestap
* @param to ending timestap
* @param status status
*/
size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)
{
struct query_count_args arg = { FDB_TSL_UNUSED, 0 };
arg.status = status;
if (!db_init_ok(db)) {
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
return FDB_INIT_FAILED;
}
fdb_tsl_iter_by_time(db, from, to, query_count_cb, &arg);
return arg.count;
}
/**
* Set the TSL status.
*
* @param db database object
* @param tsl TSL object
* @param status status
*
* @return result
*/
fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)
{
fdb_err_t result = FDB_NO_ERR;
uint8_t status_table[TSL_STATUS_TABLE_SIZE];
/* write the status will by write granularity */
_FDB_WRITE_STATUS(db, tsl->addr.index, status_table, FDB_TSL_STATUS_NUM, status, true);
return result;
}
/**
* Convert the TSL object to blob object
*
* @param tsl TSL object
* @param blob blob object
*
* @return new blob object
*/
fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)
{
blob->saved.addr = tsl->addr.log;
blob->saved.meta_addr = tsl->addr.index;
blob->saved.len = tsl->log_len;
return blob;
}
static bool check_sec_hdr_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
{
struct check_sec_hdr_cb_args *arg = arg1;
fdb_tsdb_t db = arg->db;
if (!sector->check_ok) {
FDB_INFO("Sector (0x%08" PRIX32 ") header info is incorrect.\n", sector->addr);
(arg->check_failed) = true;
return true;
} else if (sector->status == FDB_SECTOR_STORE_USING) {
if (db->cur_sec.addr == FDB_DATA_UNUSED) {
memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
} else {
FDB_INFO("Warning: Sector status is wrong, there are multiple sectors in use.\n");
(arg->check_failed) = true;
return true;
}
} else if (sector->status == FDB_SECTOR_STORE_EMPTY) {
(arg->empty_num) += 1;
arg->empty_addr = sector->addr;
if ((arg->empty_num) == 1 && db->cur_sec.addr == FDB_DATA_UNUSED) {
memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
}
}
return false;
}
static bool format_all_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
{
fdb_tsdb_t db = arg1;
format_sector(db, sector->addr);
return false;
}
static void tsl_format_all(fdb_tsdb_t db)
{
struct tsdb_sec_info sector;
sector.addr = 0;
sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, db, NULL, format_all_cb, false);
db->oldest_addr = 0;
db->cur_sec.addr = 0;
db->last_time = 0;
/* read the current using sector info */
read_sector_info(db, db->cur_sec.addr, &db->cur_sec, false);
FDB_INFO("All sector format finished.\n");
}
/**
* Clean all the data in the TSDB.
*
* @note It's DANGEROUS. This operation is not reversible.
*
* @param db database object
*/
void fdb_tsl_clean(fdb_tsdb_t db)
{
db_lock(db);
tsl_format_all(db);
db_unlock(db);
}
/**
* This function will get or set some options of the database
*
* @param db database object
* @param cmd the control command
* @param arg the argument
*/
void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
{
FDB_ASSERT(db);
switch (cmd) {
case FDB_TSDB_CTRL_SET_SEC_SIZE:
/* this change MUST before database initialization */
FDB_ASSERT(db->parent.init_ok == false);
db->parent.sec_size = *(uint32_t *)arg;
break;
case FDB_TSDB_CTRL_GET_SEC_SIZE:
*(uint32_t *)arg = db->parent.sec_size;
break;
case FDB_TSDB_CTRL_SET_LOCK:
db->parent.lock = (void (*)(fdb_db_t db))arg;
break;
case FDB_TSDB_CTRL_SET_UNLOCK:
db->parent.unlock = (void (*)(fdb_db_t db))arg;
break;
case FDB_TSDB_CTRL_SET_ROLLOVER:
/* this change MUST after database initialized */
FDB_ASSERT(db->parent.init_ok == true);
db->rollover = *(bool *)arg;
break;
case FDB_TSDB_CTRL_GET_ROLLOVER:
*(bool *)arg = db->rollover;
break;
case FDB_TSDB_CTRL_GET_LAST_TIME:
*(fdb_time_t *)arg = db->last_time;
break;
case FDB_TSDB_CTRL_SET_FILE_MODE:
#ifdef FDB_USING_FILE_MODE
/* this change MUST before database initialization */
FDB_ASSERT(db->parent.init_ok == false);
db->parent.file_mode = *(bool *)arg;
#else
FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro.");
#endif
break;
case FDB_TSDB_CTRL_SET_MAX_SIZE:
#ifdef FDB_USING_FILE_MODE
/* this change MUST before database initialization */
FDB_ASSERT(db->parent.init_ok == false);
db->parent.max_size = *(uint32_t *)arg;
#endif
break;
case FDB_TSDB_CTRL_SET_NOT_FORMAT:
/* this change MUST before database initialization */
FDB_ASSERT(db->parent.init_ok == false);
db->parent.not_formatable = *(bool *)arg;
break;
}
}
/**
* The time series database initialization.
*
* @param db database object
* @param name database name
* @param path FAL mode: partition name, file mode: database saved directory path
* @param get_time get current time function
* @param max_len maximum length of each log
* @param user_data user data
*
* @return result
*/
fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len, void *user_data)
{
fdb_err_t result = FDB_NO_ERR;
struct tsdb_sec_info sector;
struct check_sec_hdr_cb_args check_sec_arg = { db, false, 0, 0};
FDB_ASSERT(get_time);
result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_TS, user_data);
if (result != FDB_NO_ERR) {
goto __exit;
}
db->get_time = get_time;
db->max_len = max_len;
/* default rollover flag is true */
db->rollover = true;
db->oldest_addr = FDB_DATA_UNUSED;
db->cur_sec.addr = FDB_DATA_UNUSED;
/* must less than sector size */
FDB_ASSERT(max_len < db_sec_size(db));
/* check all sector header */
sector.addr = 0;
sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &check_sec_arg, NULL, check_sec_hdr_cb, true);
/* format all sector when check failed */
if (check_sec_arg.check_failed) {
if (db->parent.not_formatable) {
result = FDB_READ_ERR;
goto __exit;
} else {
tsl_format_all(db);
}
} else {
uint32_t latest_addr;
if (check_sec_arg.empty_num > 0) {
latest_addr = check_sec_arg.empty_addr;
} else {
latest_addr = db->cur_sec.addr;
/* There is no empty sector. */
latest_addr = db->cur_sec.addr = db_max_size(db) - db_sec_size(db);
}
/* db->cur_sec is the latest sector, and the next is the oldest sector */
if (latest_addr + db_sec_size(db) >= db_max_size(db)) {
/* db->cur_sec is the the bottom of the database */
db->oldest_addr = 0;
} else {
db->oldest_addr = latest_addr + db_sec_size(db);
}
}
FDB_DEBUG("TSDB (%s) oldest sectors is 0x%08" PRIX32 ", current using sector is 0x%08" PRIX32 ".\n", db_name(db), db->oldest_addr,
db->cur_sec.addr);
/* read the current using sector info */
read_sector_info(db, db->cur_sec.addr, &db->cur_sec, true);
/* get last save time */
if (db->cur_sec.status == FDB_SECTOR_STORE_USING) {
db->last_time = db->cur_sec.end_time;
} else if (db->cur_sec.status == FDB_SECTOR_STORE_EMPTY && db->oldest_addr != db->cur_sec.addr) {
struct tsdb_sec_info sec;
uint32_t addr = db->cur_sec.addr;
if (addr == 0) {
addr = db_max_size(db) - db_sec_size(db);
} else {
addr -= db_sec_size(db);
}
read_sector_info(db, addr, &sec, false);
db->last_time = sec.end_time;
}
__exit:
_fdb_init_finish((fdb_db_t)db, result);
return result;
}
/**
* The time series database deinitialization.
*
* @param db database object
*
* @return result
*/
fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db)
{
_fdb_deinit((fdb_db_t) db);
return FDB_NO_ERR;
}
#endif /* defined(FDB_USING_TSDB) */

View File

@ -0,0 +1,305 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief utils
*
* Some utils for this library.
*/
#include <stdio.h>
#include <string.h>
#include <flashdb.h>
#include <fdb_low_lvl.h>
#define FDB_LOG_TAG "[utils]"
static const uint32_t crc32_table[] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
/**
* Calculate the CRC32 value of a memory buffer.
*
* @param crc accumulated CRC32 value, must be 0 on first call
* @param buf buffer to calculate CRC32 value for
* @param size bytes in buffer
*
* @return calculated CRC32 value
*/
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size)
{
const uint8_t *p;
p = (const uint8_t *)buf;
crc = crc ^ ~0U;
while (size--) {
crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
}
return crc ^ ~0U;
}
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index)
{
size_t byte_index = ~0UL;
/*
* | write garn | status0 | status1 | status2 |
* ---------------------------------------------------------------------------------
* | 1bit | 0xFF | 0x7F | 0x3F |
* | 8bit | 0xFFFF | 0x00FF | 0x0000 |
* | 32bit | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF | 0x00FFFFFF 00FFFFFF |
*/
memset(status_table, 0xFF, FDB_STATUS_TABLE_SIZE(status_num));
if (status_index > 0) {
#if (FDB_WRITE_GRAN == 1)
byte_index = (status_index - 1) / 8;
status_table[byte_index] &= (0x00ff >> (status_index % 8));
#else
byte_index = (status_index - 1) * (FDB_WRITE_GRAN / 8);
status_table[byte_index] = 0x00;
#endif /* FDB_WRITE_GRAN == 1 */
}
return byte_index;
}
size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
{
size_t i = 0, status_num_bak = --status_num;
while (status_num --) {
/* get the first 0 position from end address to start address */
#if (FDB_WRITE_GRAN == 1)
if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) {
break;
}
#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */
if (status_table[status_num * FDB_WRITE_GRAN / 8] == 0x00) {
break;
}
#endif /* FDB_WRITE_GRAN == 1 */
i++;
}
return status_num_bak - i;
}
fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync)
{
fdb_err_t result = FDB_NO_ERR;
size_t byte_index;
FDB_ASSERT(status_index < status_num);
FDB_ASSERT(status_table);
/* set the status first */
byte_index = _fdb_set_status(status_table, status_num, status_index);
/* the first status table value is all 1, so no need to write flash */
if (byte_index == ~0UL) {
return FDB_NO_ERR;
}
#if (FDB_WRITE_GRAN == 1)
result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1, sync);
#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */
/* write the status by write granularity
* some flash (like stm32 onchip) NOT supported repeated write before erase */
result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8, sync);
#endif /* FDB_WRITE_GRAN == 1 */
return result;
}
size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num)
{
FDB_ASSERT(status_table);
_fdb_flash_read(db, addr, (uint32_t *) status_table, FDB_STATUS_TABLE_SIZE(total_num));
return _fdb_get_status(status_table, total_num);
}
/*
* find the continue 0xFF flash address to end address
*/
uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
{
uint8_t buf[32], last_data = 0x00;
size_t i, addr = start, read_size;
for (; start < end; start += sizeof(buf)) {
if (start + sizeof(buf) < end) {
read_size = sizeof(buf);
} else {
read_size = end - start;
}
_fdb_flash_read(db, start, (uint32_t *) buf, read_size);
for (i = 0; i < read_size; i++) {
if (last_data != 0xFF && buf[i] == 0xFF) {
addr = start + i;
}
last_data = buf[i];
}
}
if (last_data == 0xFF) {
return FDB_WG_ALIGN(addr);
} else {
return end;
}
}
/**
* Make a blob object.
*
* @param blob blob object
* @param value_buf value buffer
* @param buf_len buffer length
*
* @return new blob object
*/
fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)
{
blob->buf = (void *)value_buf;
blob->size = buf_len;
return blob;
}
/**
* Read the blob object in database.
*
* @param db database object
* @param blob blob object
*
* @return read length
*/
size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
{
size_t read_len = blob->size;
if (read_len > blob->saved.len) {
read_len = blob->saved.len;
}
_fdb_flash_read(db, blob->saved.addr, blob->buf, read_len);
return read_len;
}
#ifdef FDB_USING_FILE_MODE
extern fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
extern fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
extern fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size);
#endif /* FDB_USING_FILE_LIBC */
fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
if (db->file_mode) {
#ifdef FDB_USING_FILE_MODE
return _fdb_file_read(db, addr, buf, size);
#else
return FDB_READ_ERR;
#endif
} else {
#ifdef FDB_USING_FAL_MODE
fal_partition_read(db->storage.part, addr, (uint8_t *) buf, size);
#endif
}
return result;
}
fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
{
fdb_err_t result = FDB_NO_ERR;
if (db->file_mode) {
#ifdef FDB_USING_FILE_MODE
return _fdb_file_erase(db, addr, size);
#else
return FDB_ERASE_ERR;
#endif /* FDB_USING_FILE_MODE */
} else {
#ifdef FDB_USING_FAL_MODE
if (fal_partition_erase(db->storage.part, addr, size) < 0) {
result = FDB_ERASE_ERR;
}
#endif
}
return result;
}
fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
{
fdb_err_t result = FDB_NO_ERR;
if (db->file_mode) {
#ifdef FDB_USING_FILE_MODE
return _fdb_file_write(db, addr, buf, size, sync);
#else
return FDB_READ_ERR;
#endif /* FDB_USING_FILE_MODE */
} else {
#ifdef FDB_USING_FAL_MODE
if (fal_partition_write(db->storage.part, addr, (uint8_t *)buf, size) < 0)
{
result = FDB_WRITE_ERR;
}
#endif
}
return result;
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public APIs.
*/
#ifndef _FLASHDB_H_
#define _FLASHDB_H_
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include <fdb_cfg.h>
#ifdef FDB_USING_FAL_MODE
#include <fal.h>
#endif
#include <fdb_def.h>
#ifdef __cplusplus
extern "C" {
#endif
/* FlashDB database API */
fdb_err_t fdb_kvdb_init (fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
void *user_data);
void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg);
fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db);
fdb_err_t fdb_tsdb_init (fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len,
void *user_data);
void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg);
fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db);
/* blob API */
fdb_blob_t fdb_blob_make (fdb_blob_t blob, const void *value_buf, size_t buf_len);
size_t fdb_blob_read (fdb_db_t db, fdb_blob_t blob);
/* Key-Value API like a KV DB */
fdb_err_t fdb_kv_set (fdb_kvdb_t db, const char *key, const char *value);
char *fdb_kv_get (fdb_kvdb_t db, const char *key);
fdb_err_t fdb_kv_set_blob (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
size_t fdb_kv_get_blob (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
fdb_err_t fdb_kv_del (fdb_kvdb_t db, const char *key);
fdb_kv_t fdb_kv_get_obj (fdb_kvdb_t db, const char *key, fdb_kv_t kv);
fdb_blob_t fdb_kv_to_blob (fdb_kv_t kv, fdb_blob_t blob);
fdb_err_t fdb_kv_set_default (fdb_kvdb_t db);
void fdb_kv_print (fdb_kvdb_t db);
fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr);
bool fdb_kv_iterate (fdb_kvdb_t db, fdb_kv_iterator_t itr);
/* Time series log API like a TSDB */
fdb_err_t fdb_tsl_append (fdb_tsdb_t db, fdb_blob_t blob);
void fdb_tsl_iter (fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
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);
size_t fdb_tsl_query_count (fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status);
fdb_err_t fdb_tsl_set_status (fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status);
void fdb_tsl_clean (fdb_tsdb_t db);
fdb_blob_t fdb_tsl_to_blob (fdb_tsl_t tsl, fdb_blob_t blob);
/* fdb_utils.c */
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* _FLASHDB_H_ */