diff --git a/README.md b/README.md new file mode 100644 index 0000000..c44ea8e --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# TLV Serialization Format - C Language Implementation (ctlv) + +[中文](README_ZH.md) | English + +## Overview + +TLV (Type Length Value) is a data encoding format composed of the type of data (Tag), length of data (Length), and value of data (Value). This format is widely used in fields such as data communication and protocol design because it is simple, efficient, and has good scalability. + +`ctlv` is a TLV structure codec written in C language, which is commonly used in serial port, Bluetooth, and even network data transmission. + +## Usage + +- [API Reference Manual](./docs/en/API_Reference.md) +- [Example Code](./code/demo.c) + +## Contribution + +We welcome contributions to improve this project! Please follow these steps to contribute: + +1. Fork the repository. +2. Create a new branch (`git checkout -b feature/your-feature`). +3. Commit your changes (`git commit -m 'Add your feature'`). +4. Push to the branch (`git push origin feature/your-feature`). +5. Open a Pull Request. + +## License + +This project is licensed under the Apache License. See the [LICENSE](LICENSE) file for details. + +## Support + +If you have any questions or need support, please refer to the [QuecPython documentation](https://python.quectel.com/doc/en) or open an issue in this repository. \ No newline at end of file diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 0000000..9f4bb1c --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,32 @@ +# TLV序列化格式 —— C语言实现(ctlv) + +中文 | [English](README.md) + +## 概述 + + TLV(Type-Length-Value)是一种数据编码格式,由数据的类型(Tag)、数据的长度(Length)、数据的值(Value)组成。这种格式在数据通信、协议设计等领域广泛应用,因为它简单高效且具有良好的可扩展性。 + +`ctlv`是用 C 语言编写的 TLV 结构编解码器,在串口、蓝牙甚至网络数据传输中比较常用。 + +## 用法 + +- [API 参考手册](./docs/zh/API参考手册.md) +- [示例代码](./code/demo.c) + +## 贡献 + +我们欢迎对本项目的改进做出贡献!请按照以下步骤进行贡献: + +1. Fork 此仓库。 +2. 创建一个新分支(`git checkout -b feature/your-feature`)。 +3. 提交您的更改(`git commit -m 'Add your feature'`)。 +4. 推送到分支(`git push origin feature/your-feature`)。 +5. 打开一个 Pull Request。 + +## 许可证 + +本项目使用 Apache 许可证。详细信息请参阅 [LICENSE](LICENSE) 文件。 + +## 支持 + +如果您有任何问题或需要支持,请参阅 [QuecPython 文档](https://python.quectel.com/doc) 或在本仓库中打开一个 issue。 diff --git a/code/demo.c b/code/demo.c new file mode 100644 index 0000000..992ea55 --- /dev/null +++ b/code/demo.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include "tlv_types.h" +#include "tlv_iostream.h" +#include "timer.h" +#include "logging.h" + + +#define TEST_TAG_0 0x00 +#define TEST_TAG_1 0x01 +#define TEST_TAG_2 0x02 +#define TEST_TAG_3 0x03 +#define TEST_TAG_4 0x04 +#define TEST_TAG_5 0x05 +#define TEST_TAG_6 0x06 +#define TEST_TAG_7 0x07 +#define TEST_TAG_8 0x08 +#define TEST_TAG_9 0x09 + + +tlv_uint16_t test_tag[] = { + TEST_TAG_0, + TEST_TAG_1, + TEST_TAG_2, + TEST_TAG_3, + TEST_TAG_4, + TEST_TAG_5, + TEST_TAG_6, + TEST_TAG_7, + TEST_TAG_8, + TEST_TAG_9, +}; + + +char hex_mapping[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + +void bytes_to_hexstring(char *dest, tlv_uint8_t *src, tlv_uint32_t size) { + int hv, lv; + char hex_string[4] = {0x00, 0x00, 0x20, 0x00}; + for (int i = 0; i < size; i++) { + memset(hex_string, 0, 2); + hex_string[0] = hex_mapping[(src[i] >> 4) & 0x0F]; + hex_string[1] = hex_mapping[src[i] & 0x0F]; + strcat(dest, hex_string); + } +} + + +/* +* TLV test case +*/ + +static void tlv_parse_cb( + tlv_uint32_t tlv, + tlv_error_t errno, + tlv_uint8_t *original_data, + tlv_uint32_t original_data_len, + tlv_uint32_t parse_positon +) { + tlv_base_t *tlv_base; + + TLV_LOG("errno: %d", errno); + char hex_string[(original_data_len * 3) + 1]; + memset(hex_string, 0, (original_data_len * 3) + 1); + + if(errno) { + TLV_LOG("original_data_len: %d", original_data_len); + TLV_LOG("parse_positon: %d", parse_positon); + TLV_LOG("original_data: "); + bytes_to_hexstring(hex_string, original_data, original_data_len); + TLV_LOG("%s", hex_string); + } else { + while(tlv_base = tlv_get_value(tlv)) { + TLV_LOG("tlv->tag: %04X", tlv_base->tag); + TLV_LOG("tlv->len: %04X", tlv_base->length); + for(tlv_uint32_t i = 0; i < tlv_base->length; i++) { + TLV_LOG("tlv->val: %02X", tlv_base->value[i]); + } + tlv_dest_value(tlv); + TLV_LOG("------------------------"); + } + } +} + + +void main() { + uart_cfg_t uart_cfg = { + .baud_rate = 115200, + .word_length = UART_WORDLENGTH_8, + .stop_bits = 1, + .parity = UART_PARITY_NO, + .mode = UART_MODE_RX_TX, + .hardware_flowcontrol = UART_HWFC_None, + .rx_buffer_size = 1024 + }; + + tlv_uint32_t tlv = tlv_init(UART_PORT2, &uart_cfg, tlv_parse_cb, test_tag, sizeof(test_tag)/sizeof(tlv_uint16_t)); + + while (1) + { + send_data_type send_data[6] = {0}; + + tlv_chr_t test1 = 'x'; + send_data[0].pdata = (tlv_uint8_t *)&test1; + send_data[0].data_len = sizeof(test1); + send_data[0].tage = TEST_TAG_1; + + tlv_int16_t test2 = 2; + send_data[1].pdata = (tlv_uint8_t *)&test2; + send_data[1].data_len = sizeof(test2); + send_data[1].tage = TEST_TAG_2; + + tlv_int32_t test3 = 3; + send_data[2].pdata = (tlv_uint8_t *)&test3; + send_data[2].data_len = sizeof(test3); + send_data[2].tage = TEST_TAG_3; + + tlv_float_t test4 = 5.67; + send_data[3].pdata = (tlv_uint8_t *)&test4; + send_data[3].data_len = sizeof(test4); + send_data[3].tage = TEST_TAG_4; + + tlv_str_t test5 = "hello world !"; + send_data[4].pdata = (tlv_uint8_t *)test5; + send_data[4].data_len = strlen(test5); + send_data[4].tage = TEST_TAG_5; + + tlv_uint8_t test6[6] = {1, 2, 3, 4, 5, 6}; + send_data[5].pdata = test6; + send_data[5].data_len = sizeof(test6); + send_data[5].tage = TEST_TAG_6; + + // send tlv data + tlv_send(tlv, send_data, sizeof(send_data)/sizeof(send_data_type)); + + timer_msleep(1000); + } +} diff --git a/docs/en/API_Reference.md b/docs/en/API_Reference.md new file mode 100644 index 0000000..e24de5d --- /dev/null +++ b/docs/en/API_Reference.md @@ -0,0 +1,194 @@ +# TLV Serialization Format - C Language Implementation (ctlv) + +TLV (Type Length Value) is a data encoding format composed of the type of data (Tag), length of data (Length), and value of data (Value). This format is widely used in fields such as data communication and protocol design because it is simple, efficient, and has good scalability. + +`ctlv` is a TLV structure codec written in C language, which is commonly used in serial port, Bluetooth, and even network data transmission. + +## Error No. + +header file:`tlv.h` + +enum tyle:`tlv_error_t` + +enum value: + +- `NO_ERROR` +- `NOT_INITIALIZED` +- `TAG_BAD_VAL` +- `TAG_IS_INSUFF` +- `LEN_IS_INSUFF` +- `VAL_IS_INSUFF` +- `VAL_IS_OVERFLOW` +- `BUF_IS_NULL` +- `ALLOC_MEMORY_ERROR` +- `LIST_OPERATION_ERR` + +## TLV IOSTREAM + +header file:`io_stream.h` + +### TVL IOSTREAM INIT + +function define:`tlv_uint32_t tlv_init(uart_port_e port, uart_cfg_t *config, tlv_parse_callback parse_cb, tlv_uint16_t * tags_buff, tlv_uint16_t tags_num);` + +args: + +- `port`:serial port number +- `config`:serial port configure +- `parser_cb` +- `tags_buff` + +return:`tlv_ios_t` + +### get tlv value + +function define:`tlv_base_t *tlv_get_value(tlv_uint32_t tlv);` + +args: + +- `tlv`:`tlv_ios_t` + +return:`tlv_base_t` + +### del tlv value + +function define:`void tlv_dest_value(tlv_uint32_t tlv);` + +args: + +- `tlv`:`tlv_ios_t` + +return:无 + +### send TLV value + +function define:`tlv_error_t tlv_send(tlv_uint32_t tlv, send_data_type *send_data_array, tlv_uint16_t array_len);` + +args: + +- `tlv`:`tlv_ios_t` +- `send_data_array` +- `array_len`:data length + +## Parse TLV + +header file:`tlv.h` + +### create TLV parse box + +function define:`tlv_box_t *tlv_box_create(void);` + +args:None + +return:`tlv_box_t` + +### parse buffer + +function define:`tlv_error_t tlv_parse(tlv_box_t *box, tlv_uint8_t *buffer, tlv_uint32_t buffersize, tlv_uint8_t byteorder);` + +args: + +- `box`:box pointer +- `buffer`:data buffer +- `buffersize`:buffer length +- `byteorder`:bytes order(`LITTLE_ENDIAN_PLATFORM` or `BIG_ENDIAN_PLATFORM`) + +return:error no. + +### del box + +function define:`tlv_error_t tlv_box_destroy(tlv_box_t *box);` + +args: + +- `box`:box pointer + +return:error no. + +### get box buffer + +function define:`tlv_uint8_t *tlv_box_get_buffer(tlv_box_t *box);` + +args: + +- `box`:box pointer + +return: box buffer pointer + +### get box buffer length + +function define:`tlv_int32_t tlv_box_get_size(tlv_box_t *box);` + +args: + +- `box`:box pointer + +return: length + +### set TAGs + +function define:`tlv_error_t tlv_set_tags(tlv_box_t *box, tlv_uint16_t *tags_buff, tlv_uint16_t tags_num);` + +args: + +- `box`:box pointer +- `tags_buff`:TAGs +- `tags_num`:TAG numbers + +return: `tlv_error_t` + +### read tlv value + +function define:`tlv_base_t *tlv_box_get_value(tlv_box_t *box);` + +args: + +- `box`:box pointer + +return: `tlv_base_t` + +### del tlv value + +function define:`void tlv_box_dest_value(tlv_box_t *box);` + +args: + +- `box`:box pointer + +return: NULL + +### get tlv value length + +function define:`tlv_int16_t tlv_box_get_length(tlv_box_t *box, tlv_uint16_t tag);` + +args: + +- `box`:box pointer +- `tag`:TAG + +return: length + +### set TLV value + +function define:`tlv_error_t tlv_set_value(tlv_box_t *box, tlv_uint16_t tag, void *value, tlv_uint16_t length);` + +args: + +- `box`:box pointer +- `tag`: TAG +- `value`:value data pointer +- `length`:value data length + +return: `tlv_error_t` + +### serialize tlv value + +function define:`tlv_error_t tlv_box_serialize(tlv_box_t *box, tlv_uint8_t byteorder);` + +args: + +- `box`:box pointer +- `byteorder`:byte order mode + +return: `tlv_error_t` + diff --git "a/docs/zh/API\345\217\202\350\200\203\346\211\213\345\206\214.md" "b/docs/zh/API\345\217\202\350\200\203\346\211\213\345\206\214.md" new file mode 100644 index 0000000..283dc16 --- /dev/null +++ "b/docs/zh/API\345\217\202\350\200\203\346\211\213\345\206\214.md" @@ -0,0 +1,192 @@ +# TLV序列化格式 —— C语言实现(ctlv) + +TLV(Type-Length-Value)是一种数据编码格式,由数据的类型(Tag)、数据的长度(Length)、数据的值(Value)组成。这种格式在数据通信、协议设计等领域广泛应用,因为它简单高效且具有良好的可扩展性。 + +`ctlv`是用 C 语言编写的 TLV 结构编解码器,在串口、蓝牙甚至网络数据传输中比较常用。 + +## 错误码 + +头文件:`tlv.h` + +枚举类型:`tlv_error_t` + +枚举值: + +- `NO_ERROR`:无错误 +- `NOT_INITIALIZED`:未初始化 +- `TAG_BAD_VAL`:TAG非法 +- `TAG_IS_INSUFF`:TAG域解析错误(输入缓冲数据长度不足) +- `LEN_IS_INSUFF`:长度域解析错误(输入缓冲数据长度不足) +- `VAL_IS_INSUFF`:数据域解析错误(输入缓冲数据长度不足) +- `VAL_IS_OVERFLOW`:数据域解析溢出 +- `BUF_IS_NULL`:输入缓冲为空 +- `ALLOC_MEMORY_ERROR`:内存申请错误 +- `LIST_OPERATION_ERR`:TLV缓冲链表插入失败 + +## TLV流式IO + +头文件:`io_stream.h` + +### TVL初始化IO流 + +函数原型:`tlv_uint32_t tlv_init(uart_port_e port, uart_cfg_t *config, tlv_parse_callback parse_cb, tlv_uint16_t * tags_buff, tlv_uint16_t tags_num);` + +参数: + +- `port`:串口号 +- `config`:串口配置 +- `parser_cb`:解析回调函数 +- `tags_buff`:合法TAG + +返回值:tlv io句柄 + +### 获取当前TLV帧 + +函数原型:`tlv_base_t *tlv_get_value(tlv_uint32_t tlv);` + +参数: + +- `tlv`:`tlv_ios_t` + +返回值:`tlv_base_t` + +### 删除当前TLV帧 + +函数原型:`void tlv_dest_value(tlv_uint32_t tlv);` + +参数: + +- `tlv`:`tlv_ios_t` + +返回值:无 + +### 发送一帧TLV + +函数原型:`tlv_error_t tlv_send(tlv_uint32_t tlv, send_data_type *send_data_array, tlv_uint16_t array_len);` + +参数: + +- `tlv`:`tlv_ios_t` +- `send_data_array`:待发送数据 +- `array_len`:数据长度 + +## TLV解析 + +头文件:`tlv.h` + +### 创建解析暂存区 + +函数原型:`tlv_box_t *tlv_box_create(void);` + +参数:无 + +返回值:暂存区指针 + +### buffer解析 + +函数原型:`tlv_error_t tlv_parse(tlv_box_t *box, tlv_uint8_t *buffer, tlv_uint32_t buffersize, tlv_uint8_t byteorder);` + +参数: + +- `box`:暂存区指针 +- `buffer`:待解析数据指针 +- `buffersize`:数据长度 +- `byteorder`:字节对齐方式(`LITTLE_ENDIAN_PLATFORM` or `BIG_ENDIAN_PLATFORM`) + +返回值:错误码 + +### 删除暂存区 + +函数原型:`tlv_error_t tlv_box_destroy(tlv_box_t *box);` + +参数: + +- `box`:暂存区指针 + +返回值:错误码 + +### 获取暂存区buffer + +函数原型:`tlv_uint8_t *tlv_box_get_buffer(tlv_box_t *box);` + +参数: + +- `box`:暂存区指针 + +返回值:暂存区buffer指针 + +### 获取暂存区buffer长度 + +函数原型:`tlv_int32_t tlv_box_get_size(tlv_box_t *box);` + +参数: + +- `box`:暂存区指针 + +返回值:长度 + +### 设置合法TAG + +函数原型:`tlv_error_t tlv_set_tags(tlv_box_t *box, tlv_uint16_t *tags_buff, tlv_uint16_t tags_num);` + +参数: + +- `box`:暂存区指针 +- `tags_buff`:TAG数组指针 +- `tags_num`:TAG数量 + +### 读取TLV帧 + +函数原型:`tlv_base_t *tlv_box_get_value(tlv_box_t *box);` + +参数: + +- `box`:暂存区指针 + +返回值:`tlv_base_t` + +### 删除TLV帧 + +函数原型:`void tlv_box_dest_value(tlv_box_t *box);` + +参数: + +- `box`:暂存区指针 + +返回值:无 + +### 获取指定TAG的数据长度 + +函数原型:`tlv_int16_t tlv_box_get_length(tlv_box_t *box, tlv_uint16_t tag);` + +参数: + +- `box`:暂存区指针 +- `tag`:TAG + +返回值:长度 + +### 设置TLV帧 + +函数原型:`tlv_error_t tlv_set_value(tlv_box_t *box, tlv_uint16_t tag, void *value, tlv_uint16_t length);` + +参数: + +- `box`:暂存区指针 +- `tag`: TAG +- `value`:数据域指针 +- `length`:数据长度 + +返回值:错误码 + +### 序列化 + +函数原型:`tlv_error_t tlv_box_serialize(tlv_box_t *box, tlv_uint8_t byteorder);` + +参数: + +- `box`:暂存区指针 +- `byteorder`:字节对齐方式 + +返回值:无 + diff --git a/inc/tlv.h b/inc/tlv.h index a777f5e..f503e26 100644 --- a/inc/tlv.h +++ b/inc/tlv.h @@ -73,7 +73,7 @@ tlv_int32_t tlv_box_get_size(tlv_box_t *box); tlv_error_t tlv_set_tags(tlv_box_t *box, tlv_uint16_t * tags_buff, tlv_uint16_t tags_num); tlv_base_t *tlv_box_get_value(tlv_box_t *box); void tlv_box_dest_value(tlv_box_t *box); -tlv_int16_t tlv_box_get_lenth(tlv_box_t *box, tlv_uint16_t tag); +tlv_int16_t tlv_box_get_length(tlv_box_t *box, tlv_uint16_t tag); tlv_error_t tlv_set_value(tlv_box_t *box, tlv_uint16_t tag, void *value, tlv_uint16_t length); tlv_error_t tlv_box_serialize(tlv_box_t *box, tlv_uint8_t byteorder);