init repo

This commit is contained in:
ovizro 2024-11-28 16:31:00 +08:00
commit 9bf7306aa2
27 changed files with 4411 additions and 0 deletions

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
################### CMake config ###################
build
################ Executable program ################
bin/
dist/
################### VSCode config ##################
.vscode
.VSCodeCounter
####################### Tools ######################
tools/*
#################### Test config ###################
test.*
disabled.*
*.disabled
###################### Misc #######################
!.gitkeep

126
docs/extern_interface.md Normal file
View File

@ -0,0 +1,126 @@
# 通讯模块文档
本文档主要描述Voix中的用于与遥测机及上位机进行通讯及相关附加功能的模块。
这些模块拆分自原host_com.cpp文件。
## 模块公开接口
以下接口由通讯模块公开可以在VOIX主程序中使用。
1. `void extern_interface_init(const char* config_path, et_callback_t cb)`:模块初始化函数
- `config_path`:配置文件路径
- `cb`:回调函数,用于进行指令响应和遥测数据请求
2. `void (*et_callback_t)(uint32_t type, size_t len, union PacketData data)`:回调函数类型,内部数据为指针类型的数据包仅保证在回调函数中有效。
- `type`:数据包类型
- `len`数据包长度对于指针类型数据包len为指针指向的数据长度对于字符串类型数据包len为字符串包含后缀`\0`的长度。
- `data`:数据包数据
3. `union PacketData`:数据包定义
- `pd_integer`int64_t类型的数据
- `pd_float`double类型的数据
- `pd_text`const char*类型的数据
- `pd_pointer`void*类型的数据,
4. 数据包类型(宏定义):
- `ET_TYPE_NONE`:无数据
- `ET_TYPE_TEXT`文本对应const char*类型的数据
- `ET_TYPE_AUDIO`音频对应short*类型的数据
- `ET_TYPE_COMMAND`指令码对应uint8_t[6]类型的数据
- `ET_TYPE_ALARM`告警码对应uint8_t[4]类型的数据
- `ET_TYPE_TELEMETRY_REQUEST`遥测请求对应void*类型的数据,需要主程序在数据包内的指针中写入对应的变量数据
5. `void send_data_packet(uint32_t type, size_t len, union PacketData data)`:发送数据包
- `type`:数据包类型
- `len`数据包长度对于指针类型数据包len为指针指向的数据长度对于字符串类型数据包len为字符串包含后缀`\0`的长度。
- `data`:数据包数据
## 外部依赖
通讯模块需要一些来自主程序的全局变量,主要用于生成遥测报文。
- `g_bKeepSysRuning`:系统运行状态标志
- `g_bCloseASR`ASR关闭状态标志
- `g_iSysState`:系统状态
- `g_bVolumeKeyPressed`:静音按键状态标志
- `g_bWakeupKeyPressed`:唤醒按键状态标志
- `g_iVolumeGrade`:音量等级
- `g_iSysVerHigh`系统版本号高8位
- `g_iSysVerLow`系统版本号低8位
- `g_iAppVerHigh`应用版本号高8位
- `g_iAppVerLow`应用版本号低8位
以上全局变量通过回调函数中的遥测请求数据包获取。
## Transport模块
用于进行底层通讯的模块包含comframe库、通讯线程及回调定义接口的封装。
主程序需要构建回调函数定义数组,并通过线程函数的参数传入。
### 公开接口
1. `void* transport_thread(void* arg)`:线程函数
- `arg`:需要传入以零项作为结尾的`struct ComFrameTypeDef`结构体数组
2. `struct ComFrameTypeDef`:回调函数定义
- `tp_id`数据类型与数据帧中的type对应
- `tp_callback`:回调函数指针
- `tp_flag`:类型回调标签,如 是否进行校验
3. `void (*tp_callback_t)(struct ComFrameTypeDef*, struct Comframe*)`:回调函数
- `struct ComFrameTypeDef*`:回调函数定义结构体指针
- `struct Comframe*`:数据帧
4. 回调标签(宏定义):
- `TP_FLAG_DEFAULT`:默认回调标签
- `TP_FLAG_NOVERIFY`:不进行数据校验
- `TP_FLAG_THREAD`:在独立线程中执行回调函数
- `TP_FLAG_DISABLE`:禁用回调函数
## Telemetry模块
遥测模块,用于获取并同步遥测信息,并提供遥测回调函数。
### 公开接口
1. `void telemetry_handler(struct ComFrameTypeDef*, struct Comframe*)`:遥测请求回调函数
### 内部接口
1. `struct Telemetry`:遥测数据(遥测串口)
2. `struct UpperHostTelemetry`:遥测数据(上位机串口)
3. `void flush_telemetry_data()`:更新遥测数据(遥测串口)
4. `void flush_upper_host_telemetry_data()`:更新遥测数据(上位机串口)
## Command模块
用于定义命令回调函数在transport模块中调用。
### 公开接口
1. `void command_handler(struct ComFrameTypeDef*, struct Comframe*)`:命令回调函数
### 内部接口
1. `struct CommandDef`:命令定义
- `cmd_id`命令ID
- `cmd_callback`:回调函数指针
2. `void (*cmd_callback_t)(uint8_t[])`:命令回调函数
## Alarm模块
用于定义告警回调函数在transport模块中调用。
### 公开接口
1. `void alarm_handler(struct ComFrameTypeDef*, struct Comframe*)`:告警回调函数
### 内部接口
1. `struct AlarmDef`:告警定义
- `alarm_id`告警ID
- `alarm_text`:告警文本
## TypeHandler模块
用于定义除遥测请求及告警信息外的其他回调函数在transport模块中调用。
### 公开接口
1. `void speech_injected_handler(struct ComFrameTypeDef*, struct Comframe*)`:语音注入回调函数
2. `void audio_handler(struct ComFrameTypeDef*, struct Comframe*)`:语音数据回调函数
3. `void grant_time_handler(struct ComFrameTypeDef*, struct Comframe*)`:授时回调函数

173
include/c_testcase.h Normal file
View File

@ -0,0 +1,173 @@
#ifndef _INCLUDE_C_TESTCASE_
#define _INCLUDE_C_TESTCASE_
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
#include <iostream>
extern "C"{
#endif
#define SKIP_RET_NUMBER (*((const int*)"SKIP"))
#define SKIP_TEST do { return SKIP_RET_NUMBER; } while (0)
#define END_TEST do { return 0; } while (0)
#define TEST_CASE(name) \
int name (); \
int _tc_tmp_ ## name = _add_test_case(# name, name); \
int name ()
#define INTERACTIVE \
int interactive_impl(int argc, const char** argv); \
int _tc_s_tmp_interactive = _set_interactive(interactive_impl); \
int interactive_impl(int argc, const char** argv)
#define SETUP \
int setup_impl(const char* test_case_name); \
int _tc_s_tmp_setup = _set_setup(setup_impl); \
int setup_impl(const char* test_case_name)
#define TEARDOWN \
int teardown_impl(const char* test_case_name); \
int _tc_s_tmp_teardown = _set_teardown(teardown_impl); \
int teardown_impl(const char* test_case_name)
#undef assert
#ifdef __cplusplus
#define assert(expr) do { \
if (!(static_cast <bool> (expr))) { \
::std::cout << "assert failed: " #expr "\n" << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#define assert_eq(expr1, expr2) do { \
if ((expr1) != (expr2)) { \
::std::cout << "assert failed: " #expr1 " == " #expr2 "\n" << ::std::endl; \
::std::cout << "\t#0: " << (expr1) << ::std::endl; \
::std::cout << "\t#1: " << (expr2) << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#define assert_ne(expr1, expr2) do { \
if ((expr1) == (expr2)) { \
::std::cout << "assert failed: " #expr1 " != " #expr2 "\n" << ::std::endl; \
::std::cout << "\t#0: " << (expr1) << ::std::endl; \
::std::cout << "\t#1: " << (expr2) << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#define assert_gt(expr1, expr2) do { \
if ((expr1) <= (expr2)) { \
::std::cout << "assert failed: " #expr1 " > " #expr2 "\n" << ::std::endl; \
::std::cout << "\t#0: " << (expr1) << ::std::endl; \
::std::cout << "\t#1: " << (expr2) << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#define assert_ls(expr1, expr2) do { \
if ((expr1) >= (expr2)) { \
::std::cout << "assert failed: " #expr1 " < " #expr2 "\n" << ::std::endl; \
::std::cout << "\t#0: " << (expr1) << ::std::endl; \
::std::cout << "\t#1: " << (expr2) << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#define assert_ge(expr1, expr2) do { \
if ((expr1) < (expr2)) { \
::std::cout << "assert failed: " #expr1 " >= " #expr2 "\n" << ::std::endl; \
::std::cout << "\t#0: " << (expr1) << ::std::endl; \
::std::cout << "\t#1: " << (expr2) << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#define assert_le(expr1, expr2) do { \
if ((expr1) > (expr2)) { \
::std::cout << "assert failed: " #expr1 " <= " #expr2 "\n" << ::std::endl; \
::std::cout << "\t#0: " << (expr1) << ::std::endl; \
::std::cout << "\t#1: " << (expr2) << ::std::endl; \
::std::cout << "file: \"" __FILE__ "\", line " << __LINE__ << ", in " << __ASSERT_FUNCTION << "\n" << ::std::endl;\
test_case_abort(1); \
} \
} while (0)
#else
#define assert(expr) do { \
if (!((bool)(expr))) { \
printf("assert failed: " #expr "\n"); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
#endif
#define assert_i32_eq(expr1, expr2) do { \
if ((int32_t)(expr1) != (uint32_t)(expr2)) { \
printf("assert failed: " #expr1 " == " #expr2 "\n"); \
printf("\t\t%d\t!=\t%d\n", (int32_t)(expr1), (int32_t)(expr2)); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
#define assert_i32_ne(expr1, expr2) do { \
if ((int32_t)(expr1) == (int32_t)(expr2)) { \
printf("assert failed: " #expr1 " != " #expr2 "\n"); \
printf("\t\t%d\t==\t%d\n", (int32_t)(expr1), (int32_t)(expr2)); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
#define assert_i64_eq(expr1, expr2) do { \
if ((expr1) != (expr2)) { \
printf("assert failed: " #expr1 " == " #expr2 "\n"); \
printf("\t\t%lld\t!=\t%lld\n", (int64_t)(expr1), (int64_t)(expr2)); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
#define assert_i64_ne(expr1, expr2) do { \
if ((expr1) == (expr2)) { \
printf("assert failed: " #expr1 " != " #expr2 "\n"); \
printf("\t\t%lld\t==\t%lld\n", (int64_t)(expr1), (int64_t)(expr2)); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
#define assert_str_eq(expr1, expr2) do { \
if (strcmp((expr1), (expr2)) != 0) { \
printf("assert failed: " #expr1 " == " #expr2 "\n"); \
printf("\t#0: %s\n", (expr1)); \
printf("\t#1: %s\n", (expr2)); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
#define assert_str_ne(expr1, expr2) do { \
if (strcmp((expr1), (expr2)) == 0) { \
printf("assert failed: " #expr1 " != " #expr2 "\n"); \
printf("\t#0: %s\n", (expr1)); \
printf("\t#1: %s\n", (expr2)); \
printf("file: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\
test_case_abort(1); \
} \
} while (0)
typedef int (*test_case)();
typedef int (*interactive_func)(int argc, const char** argv);
typedef int (*context_func)(const char* name);
int _add_test_case(const char* name, test_case func);
int _set_interactive(interactive_func func);
int _set_setup(context_func func);
int _set_teardown(context_func func);
void __attribute__((noreturn)) test_case_abort(int exit_code);
#ifdef __cplusplus
}
#endif
#endif

240
include/comframe.h Executable file
View File

@ -0,0 +1,240 @@
#ifndef _INCLUDE_FRAME_
#define _INCLUDE_FRAME_
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "defines.h"
//#define COM_FRAME_DEBUG
#define byteswapl(x) (0xff000000 & x << 24) \
|(0x00ff0000 & x << 8) \
|(0x0000ff00 & x >> 8) \
|(0x000000ff & x >> 24)
#define byteswaps(x) (0xff00 & x << 8) \
|(0x00ff & x >> 8)
#ifdef __cplusplus
extern "C" {
#endif
struct ComFrameHeader {
uint16_t magic_number;
uint16_t address;
uint16_t type;
uint16_t length;
} __attribute__((packed));
typedef struct _ComFrame {
struct ComFrameHeader header;
uint8_t payload[COM_FRAME_DEFAULT_PAYLOAD_LENGTH];
} ComFrame;
#define ComFrame_HEADER(frame) ((struct ComFrameHeader*)(frame))
#define ComFrame_ADDRESS(frame) (ComFrame_HEADER(frame)->address)
#define ComFrame_TYPE(frame) (ComFrame_HEADER(frame)->type)
#define ComFrame_PAYLOAD(frame) (frame->payload)
#define ComFrame_PAYLOAD_LENGTH(frame) (ComFrame_HEADER(frame)->length - 1)
#define ComFrame_PAYLOAD_ALIGNED_LENGTH(frame) (ComFrame_HEADER(frame)->length & (~1))
#define ComFrame_CHECKSUM(frame) (((uint16_t*)&(frame)->payload)[ComFrame_HEADER(frame)->length >> 1])
#define ComFrame_LENGTH(frame) (sizeof(ComFrame) + ComFrame_PAYLOAD_ALIGNED_LENGTH(frame) - COM_FRAME_DEFAULT_PAYLOAD_LENGTH + sizeof(uint16_t))
/// @brief init frame header struct
/// @param header header struct
/// @param address device address
/// @param type frame type
/// @param length frame payload length + 1
static __inline void ComFrameHeader_Init(struct ComFrameHeader* header, uint16_t address, uint16_t type, uint16_t length) {
header->magic_number = COM_FRAME_MAGIC_NUMBER;
header->address = address;
header->type = type;
header->length = length;
}
/// @brief swap haeder structure endianness
/// @param header header struct
static __inline void ComFrameHeader_SwapEndian(struct ComFrameHeader* header) {
header->magic_number = byteswaps(header->magic_number);
header->address = byteswaps(header->address);
header->type = byteswaps(header->type);
header->length = byteswaps(header->length);
}
/// @brief check magic number of header struct
/// @param header header struct
/// @param swap_endian whether to switch endianness
/// @return structure validity, true means invalid
static __inline bool ComFrameHeader_Check(const struct ComFrameHeader* header, const bool swap_endian) {
uint16_t number = header->magic_number;
if (swap_endian) {
number = byteswaps(number);
}
if (number != COM_FRAME_MAGIC_NUMBER) {
return true;
}
number = header->address;
if (swap_endian) {
number = byteswaps(number);
}
if (number != COM_FRAME_ADDRESS_VOIX) {
return true;
}
if (ComFrame_LENGTH(header) < 12) {
return true;
}
number = header->type;
if (swap_endian) {
number = byteswaps(number);
}
// if ( // commented by gejp 20201007
// number != COM_FRAME_TYPE_COMMAND &&
// number != COM_FRAME_TYPE_SPEECH_INJECTED &&
// number != COM_FRAME_TYPE_SPEECH_TEXT &&
// number != COM_FRAME_TYPE_SPEECH_PCM &&
// number != COM_FRAME_TYPE_AUDIO &&
// number != COM_FRAME_TYPE_REQUEST &&
// number != COM_FRAME_TYPE_TELEMETRY_REQUEST
// ) {
// return true;
// }
return false;
}
/// @brief create new com frame uninitialized
/// @param payload_length length of payload
/// @return new uinitialized frame
static __inline ComFrame* ComFrame_NewUninited(uint16_t payload_length) {
if (payload_length & 1) {
payload_length += 1;
}
return (ComFrame*)malloc(sizeof(ComFrame) + payload_length - COM_FRAME_DEFAULT_PAYLOAD_LENGTH + sizeof(uint16_t));
}
/// @brief get frame checksum
/// @param frame com frame struct
/// @param swap_endian whether to switch endianness
/// @return checksum
static __inline uint16_t ComFrame_Checksum(const ComFrame* frame, const bool swap_endian) {
const uint16_t* buffer = (const uint16_t*)&frame->payload;
uint16_t length = ComFrame_HEADER(frame)->length;
if (swap_endian) {
length = byteswaps(length);
}
return buffer[length >> 1];
}
/// @brief get frame length
/// @param frame com frame struct
/// @param swap_endian whether to switch endianness
/// @return length of the frame
static __inline uint32_t ComFrame_Length(const ComFrame* frame, const bool swap_endian) {
uint16_t length = ComFrame_HEADER(frame)->length;
if (swap_endian) {
length = byteswaps(length);
}
length &= (~1);
return sizeof(ComFrame) + length - COM_FRAME_DEFAULT_PAYLOAD_LENGTH + sizeof(uint16_t);
}
/// @brief destroy the frame
/// @param frame the frame to destroy
static __inline void ComFrame_Del(ComFrame* frame) {
if (frame) free(frame);
}
/// @brief flush receive buffer
/// @param offset the size used, usually the length of the data frame
/// @param buffer receive buffer
/// @param p_cached_size cache size to reset, not null
/// @note please set the offset to 0 after calling the function if used with ComFrame_ReceiveEx
static __inline void ComFrame_ReceiveFlushBuffer(size_t offset, uint8_t* buffer, size_t* p_cached_size) {
*p_cached_size -= offset;
memmove(buffer, buffer + offset, *p_cached_size);
}
/// @brief calculate the checksum of the specified payload
/// @param header header struct (must be little endian)
/// @param payload payload data
/// @return checksum
uint16_t ComFramePayload_EvalChecksum(const struct ComFrameHeader* header, const void* payload);
/// @brief verify the checksum of the specified payload
/// @param header header struct (must be little endian)
/// @param payload payload data
/// @param checksum checksum to verify
/// @param swap_checksum_endian whether to switch checksum endianness
/// @return payload validity, true means invalid
bool ComFramePayload_VerifyChecksum(const struct ComFrameHeader* header, const void* payload, uint16_t checksum);
/// @brief create new com frame from header and known checksum
/// @param header header struct (must be little endian)
/// @param payload payload data
/// @param checksum payload checksum
/// @param swap_endian whether to switch endianness
/// @return new frame
ComFrame* ComFrame_FromHeaderEx(const struct ComFrameHeader* header, const void* payload, uint16_t checksum, const bool swap_endian);
/// @brief create new com frame from header
/// @param header header struct (must be little endian)
/// @param payload payload data
/// @param swap_endian whether to switch endianness
/// @return new frame
ComFrame* ComFrame_FromHeader(const struct ComFrameHeader* header, const void* payload, const bool swap_endian);
/// @brief create new com frame
/// @param address device address
/// @param type frame type
/// @param payload payload data
/// @param payload_length payload data real length
/// @param swap_endian whether to switch endianness
/// @return new frame
ComFrame* ComFrame_New(uint16_t address, uint16_t type, const void* payload, uint16_t payload_length, const bool swap_endian);
/// @brief update frame payload and checksum
/// @param frame com frame struct
/// @param payload payload data or NULL, if set to NULL, the payload will not be updated.
/// @param payload_length payload data real length
/// @param swap_endian whether to switch endianness
void ComFrame_UpdatePayload(ComFrame* frame, const void* payload, const uint16_t payload_length, const bool swap_endian);
/// @brief verify frame checksum
/// @param frame con frame struct
/// @param swap_endian whether to switch endianness
/// @return structure validity, true means invalid
bool ComFrame_Verify(const ComFrame* frame, const bool swap_endian);
/// @brief send a frame to com
/// @param tty_id com tty id
/// @param frame con frame struct
/// @param swap_endian whether to switch endianness
/// @return error number, 0 means ok
ssize_t ComFrame_Send(int tty_id, const ComFrame* frame, const bool swap_endian);
/// @brief receive a frame from com
/// @param tty_id com tty id
/// @param buffer_size receive buffer size
/// @param swap_endian whether to switch endianness
/// @return new frame (little endian) or NULL
ComFrame* ComFrame_Receive(int tty_id, const size_t buffer_size, const bool swap_endian);
/// @brief receive a frame from com
/// @param tty_id com tty id
/// @param buffer buffer pointer, create a new one when it is NULL
/// @param buffer_size receive buffer size
/// @param p_offset offset will be set if not null
/// @param p_cached_size cached size will be set if not null
/// @param p_run_flag run flag pointer, if the inner date is set to false, the function will stop and return NULL
/// @param swap_endian whether to switch endianness
/// @return frame (little endian) in buffer or NULL
ComFrame* ComFrame_ReceiveEx(int tty_id, void* buffer, const size_t buffer_size, size_t* p_offset, size_t* p_cached_size, bool* p_run_flag, const bool swap_endian);
//int get_com_tty_id(const char* path, unsigned int baudRate);
//void init_tty(int tty_id, unsigned int baudRate);
#ifdef __cplusplus
}
#endif
#endif

20
include/command.h Executable file
View File

@ -0,0 +1,20 @@
#ifndef SYS_COMMAND_H_
#define SYS_COMMAND_H_
#include <stdint.h>
#include <stdbool.h>
bool is_command(uint8_t* array, uint8_t* value, int size);
//--
uint8_t command_open_asr[] = { 0x00, 0xf0, 0xaa, 0xaa, 0xaa, 0xaa };
uint8_t command_close_asr[] = { 0x00, 0xf2, 0xaa, 0xaa, 0xaa, 0xaa };
uint8_t command_open_call[] = { 0x00, 0xf4, 0xaa, 0xaa, 0xaa, 0xaa };
uint8_t command_close_call[] = { 0x00, 0xf6, 0xaa, 0xaa, 0xaa, 0xaa };
uint8_t command_wakeup[] = { 0x00, 0x01, 0x02, 0x27, 0x02, 0x27 };
uint8_t command_sleep[] = { 0x00, 0x01, 0x02, 0x28, 0x02, 0x28 };
uint8_t command_volume[] = { 0x00, 0x01, 0x02, 0x29, 0x02, 0x29};//2024.10.22{0x00, 0xf8, 0xaa, 0xaa, 0xaa, 0xaa };
uint8_t command_reset[] = { 0x00, 0x01, 0x02, 0x2A, 0x02, 0x2A};//2024.10.22 { 0x00, 0xfa, 0xaa, 0xaa, 0xaa, 0xaa };
#endif

58
include/dataqueue.hpp Normal file
View File

@ -0,0 +1,58 @@
#ifndef _INCLUDED_QUEUE_
#define _INCLUDED_QUEUE_
#include <list>
#include <mutex>
#include <condition_variable>
template <typename T>
class DataQueue {
public:
DataQueue(size_t init_size = 0) : m_Queue(init_size)
{}
DataQueue(DataQueue&) = delete;
void Push(T data)
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Queue.push_back(data);
m_Cond.notify_one();
}
T Pop()
{
std::unique_lock<std::mutex> lock(m_Mutex);
while (m_Queue.empty())
{
m_Cond.wait(lock);
}
T data = m_Queue.front();
m_Queue.pop_front();
return data;
}
bool Empty()
{
std::lock_guard<std::mutex> lock(m_Mutex);
return m_Queue.empty();
}
size_t Size()
{
std::lock_guard<std::mutex> lock(m_Mutex);
return m_Queue.size();
}
void Clear()
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Queue.clear();
}
protected:
std::list<T> m_Queue;
std::mutex m_Mutex;
std::condition_variable m_Cond;
};
#endif

255
include/defines.h Executable file
View File

@ -0,0 +1,255 @@
#ifndef VOIX_DEFINES_H
#define VOIX_DEFINES_H
#include <queue>
#include <set>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <cstring>
#include <netinet/in.h>
#include <atomic>
#include <map>
#include "CCfgFileParser.h"
#include "dataqueue.h"
#include "log.h"
using namespace std;
#define PrintFilePos() //printf("\nfile:%s, line:%2d---",__FILE__,__LINE__);
#define CHUNK_SIZE 512 // 512 samples per frame
#define SAMPLE_RATE 16000 // ReSpeaker Mic Array V2.0 support SampleRate from 16kHz to 48kHz
#define RECORD_SECONDS 200000
#define ASR_RESULT_LEN 1024
#define WAKE_CHUNK_SIZE 512
#define ASR_CHUNCK_SIZE 8000
#define CHUNK_SIZE 512 // 512 samples per frame
#define SAMPLE_RATE 16000 // ReSpeaker Mic Array V2.0 support g_iSampleRate from 16kHz to 48kHz
#define RECORD_SECONDS 200000
#define ASR_RESULT_LEN 1024
#define ASR_CHUNCK_SIZE 8000
#define LAST_FRAME_ENERGY_ARRAY_SIZE 15
//-------------------host telemetry defines start --------------------------
#define COM_FRAME_MAGIC_NUMBER 0xEB90
#define COM_FRAME_DEFAULT_PAYLOAD_LENGTH 2
#define COM_FRAME_PADDING_DATA 0xAA
#define COM_FRAME_TYPE_COMMAND 0x00C0
#define COM_FRAME_TYPE_SPEECH_INJECTED 0x00D0
#define COM_FRAME_TYPE_SPEECH_TEXT 0x00D0
#define COM_FRAME_TYPE_SPEECH_PCM 0x00D1
#define COM_FRAME_TYPE_AUDIO 0x00D1
#define COM_FRAME_TYPE_REQUEST 0x0090
#define COM_FRAME_TYPE_TELEMETRY_REQUEST 0x00B0
#define COM_FRAME_TYPE_TELEMETRY_ANSWER 0x0070
#define COM_FRAME_TYPE_TELEMETRY_ERROR 0x0071
#define COM_FRAME_ADDRESS_VOIX 0x011A
// added by gejp start 2024.10.03
#define COM_FRAME_TYPE_GRANT_TIME 0x0085
#define COM_FRAME_TYPE_VOICE_ANNOUNCEMENT_AND_ALARM_CODE_REQUEST 0x0083
#define COM_FRAME_TYPE_VOICE_ANNOUNCEMENT_AND_ALARM_CODE_RESPONSE 0x0083
#define CMD_CODE_TELEMETRY_LOW_CABIN_TEMPERATURE 0x2F01
#define CMD_TEXT_TELEMETRY_LOW_CABIN_TEMPERATURE "设置低舱温"
#define CMD_CODE_TELEMETRY_MODERATE_CABIN_TEMPERATURE 0x2F02
#define CMD_TEXT_TELEMETRY_MODERATE_CABIN_TEMPERATURE "设置舱温适中"
#define CMD_CODE_TELEMETRY_HIGH_CABIN_TEMPERATURE 0x2F04
#define CMD_TEXT_TELEMETRY_HIGH_CABIN_TEMPERATURE "设置高舱温"
#define CMD_CODE_TURN_OFF_AMBIENT_LIGHT 0x2F07
#define CMD_TEXT_TURN_OFF_AMBIENT_LIGHT "关氛围灯"
#define CMD_CODE_TURN_ON_AMBIENT_LIGHT 0x2F08
#define CMD_TEXT_TURN_ON_AMBIENT_LIGHT "开氛围灯"
#define CMD_CODE_CONTROL_TOP_LIGHT 0x2F0B
#define CMD_TEXT_CONTROL_TOP_LIGHT "控制顶部照明灯"
#define CMD_CODE_CONTROL_SHOULDER_LIGHT 0x2F0D
#define CMD_TEXT_CONTROL_SHOULDER_LIGHT "控制肩部照明灯"
#define CMD_CODE_USE_RESTROOM 0x2F0E
#define CMD_TEXT_USE_RESTROOM "使用卫生间"
#define CMD_CODE_RESTROOM_USE_FINISHED 0x2F10
#define CMD_TEXT_RESTROOM_USE_FINISHED "卫生间使用结束"
extern std::map<std::string, uint16_t> g_telemetryText2Code;
//==============Queue============================================
typedef void* voidPtr;
extern void* g_pStatusLog, * g_pProcessLog, * g_pResultLog, * g_pDebugLog;
//==========================================================
extern void** g_pDataQueue;
extern void* g_pRecordQueue;
extern volatile bool g_bCloseASR;
extern volatile bool g_bCall;
extern volatile uint32_t g_iPlayedCallFrameCnt, g_iPlayedCallSampleCnt;
extern volatile uint32_t g_iCallRcvFrameCnt;//本次通话接收的帧数量
extern volatile uint32_t g_iInsertSlientSegCnt;
extern uint8_t g_iAlarmCode[4]; // 报警码 gejp 2024.10.03
extern uint8_t g_i5cmdNum[5]; // 5 info of cmd in history added by gejp 2024.10.03
extern uint16_t g_i5cmdCode[5];
extern uint8_t g_i5cmdOutcodes[5][6];
extern uint8_t g_i5cmdSendAbsTime[5][5];
extern uint32_t g_iCmdCount;
// gejp 2024.10.09
extern uint8_t g_iSysVerHigh;
extern uint8_t g_iSysVerLow;
extern uint8_t g_iAppVerHigh;
extern uint8_t g_iAppVerLow;
// gejp 2024.10.10
extern uint8_t g_iEnableAlarmCode;
// tangyc 2024.10.25
extern long g_iExternTimeDifference;
#define getCodeInfo() getCodeLine(__FILE__, __LINE__)
extern bool g_bPrintResultLog;
extern int32_t g_outputBaseNum;
//int LoadLogQueueModule(char* strModulePath);
int32_t openLogAndQueues(char *config_file_path);
void ProcQueueFull(int i, bool bClear = true);
extern int g_iQueueCnt;
extern long g_iRcvNackedPCM;
extern bool g_bGPIO_Open;
extern int g_iGPIOWakeNum;
extern int g_iGPIOVolumeNum;
extern int g_iGPIOWakePressTime;
extern bool g_bTelemetry_Open;
extern bool g_bHostInfoTrans_Open;
extern int g_iUseTeleComForTelemetry;
extern int g_iUseHostComForTelemetry;
extern double g_fSilEnergy;
extern long g_iMicDataQueueMB;
extern void* g_pLogQueueModuleHandle;
extern long g_iPcmBlockBytes;
extern long g_iCallBytesPerBlockMS;
extern long g_iCallSampleRateMS;
extern char g_strPCM_ComPath[128];
extern int g_iPcmCom_tty_id;
extern long g_iPCMCom_BaudRate;
extern char g_strHostInfo_ComPath[128];
extern int g_iHostCom_tty_id;
extern long g_iHostInfoCom_BaudRate;
extern char g_strTelemetry_ComPath[128];
extern int g_iTelemetry_Com_tty_id;
extern long g_iTelemetryCom_BaudRate;
enum QueueIndex { // 数据类型
RECORD_DATA_QUEUE = 0,
PCM_QUEUE_FOR_KWS,
PCM_QUEUE_FOR_ASR,//
TEXT_QUEUE_FOR_TTS,//
TEXT_QUEUE_FOR_TTS_URGENT,
AUDIO_QUEUE_FOR_PLAY,//
AUDIO_QUEUE_FOR_PLAY_URGENT,
ORDER_INFO_QUEUE,// to upper host
ASSISTANT_INFO_QUEUE,// to upper host
HOST_INFO_QUEUE, //from upper host
AUDIO_FOR_HOST, // to upper host
DATA_QUEUE_COUNT
};
//extern void* SystemTimer_old(void* p);
extern void* SystemTimer(void* p);
extern char g_strCurTime[50], g_strStartTime[50];
//------------ input audio parameters -----------------------
extern long g_iInputType;// 0--com 1--mic 2--udp
extern char g_strRcvPCM_UDP_IP[64];
extern long g_iUdpPort4RcvPCM;
extern char g_strPCM_ComPath[128];
extern long g_iPCMCom_BaudRate;
extern long g_iPCMCom_Proto;
extern int g_iPcmCom_tty_id;
//------------ upper host parameters -----------------------
extern long g_iHostInfoTransMode; //0--com, 1--udp, 2--unix socket
extern char g_strHostInfo_ComPath[128];
extern long g_iHostInfoCom_BaudRate;
extern int g_iHostCom_tty_id;
extern char g_strSndUnixSocket[500];
extern char g_strRcvUnixSocket[500];
extern char g_strUpperHostIP[64];
extern long g_iSndUdpPort4HostInfo; //port of upper host to receive
extern long g_iRcvUdpPort4HostInfo; //port to receive upper host info
//---------------------------------------------------------------
extern long g_iWakeWhenStart;
extern long g_iStatusLogInterval;
//extern volatile long g_iConfirmRemainSeconds;//remain time to finish confirm, seconds
//extern volatile long g_iNoEffectiveResultSeconds;// record total time lenght there is no effective result
extern volatile uint64_t g_iLastWakeTime;
extern volatile uint64_t g_iLastResultTime;
extern volatile uint64_t g_iEndPlayTime;
//extern volatile uint64_t g_iToSleepTime;
extern volatile int g_iWaitType;
extern volatile uint64_t g_iWaitTimeMS[5];
extern volatile long g_iSilentSampleCnt, g_iSoundSampleCnt;
extern volatile long g_iSilentSampleCnt2FetchResul;
extern char g_strSystemLogPath[500];
extern char g_strPCMSavePath[500];
extern struct tm g_tCurLocalTime;
extern bool g_bKeepSysRuning;
extern long g_iSaveInputAudio;
extern long g_iSaveTTSAudio;
extern long g_iSaveHostAudio;
extern long g_iSavePlaySound;
extern long g_iPlaySound;
extern uint32_t g_iQueueFullTimes[DATA_QUEUE_COUNT];
extern volatile uint64_t g_iComRcvBytes[2];
extern long g_iUseASR2Wake;
extern void set_signal_handler(int n);
void setSysSleep(char* pSrc);//pSrc: by whom, kws,asr, button or start
void setSysWake(char *pSrc);//pSrc: by whom, kws,asr, button or start
void SayHello(char* pNoticeStr);
extern std::atomic<int> g_iSysState;
extern bool g_bKeepSysRunning;
extern unsigned int g_iOrderCnt;
extern unsigned int g_iOrderExeSuccCnt;
extern unsigned int g_iOrderExeFailCnt;
extern unsigned int g_iOrderSndSuccCnt;
extern unsigned int g_iOrderSndFailCnt;
extern unsigned int g_iOrderResultCnt;
extern unsigned int g_iAssistantCnt;
extern unsigned int g_iQuerySndSuccCnt;
extern unsigned int g_iQuerySndFailCnt;
extern unsigned int g_iQueryResultCnt;
extern unsigned int g_iRcvResultErr[2];
extern uint16_t volatile g_iTelemetryOrderSndSuccCnt;
extern uint8_t volatile g_last_send_command[6];
//extern int64_t g_iPreWakeTime;
extern std::atomic<int> g_iOrdinaryPlayStop;
extern bool g_bVolumeKeyState;
extern bool g_bWakeupKeyState;
extern bool g_bVolumeKeyPressed;
extern bool g_bWakeupKeyPressed;
#endif //AUDIOSERVER_AUDIODATAUNIT_H

19
include/event.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _INCLUDE_EVENT_H_
#define _INCLUDE_EVENT_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*event_callback)(const char*, void*, void*);
void sys_event(const char *event_name, void *args);
int sys_event_register(const char *event_name, event_callback callback, void* user_data);
int sys_event_unregister(const char *event_name, event_callback callback);
void* sys_event_thread(void*);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,49 @@
#ifndef _INCLUDE_EXTERN_INTERFACE_H_
#define _INCLUDE_EXTERN_INTERFACE_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define ET_TYPE_NONE 0
#define ET_TYPE_TEXT 1
#define ET_TYPE_AUDIO 2
#define ET_TYPE_COMMAND 3
#define ET_TYPE_ALARM 4
#define ET_TYPE_TELEMETRY_REQUEST 5
#ifdef __cplusplus
extern "C" {
#endif
struct TelemetryRequestData {
bool keep_sys_running;
bool close_asr;
int sys_state;
bool volume_key_pressed;
bool wakeup_key_pressed;
int volume_grade;
int sys_ver_high;
int sys_ver_low;
int app_ver_high;
int app_ver_low;
};
union PacketData
{
int64_t pd_integer;
double pd_float;
const char* pd_text;
void* pd_pointer;
};
typedef void (*et_callback_t)(uint32_t, size_t, union PacketData);
void extern_interface_init(const char* config_path, et_callback_t cb);
void extern_interface_send(uint32_t type, size_t len, union PacketData data);
#ifdef __cplusplus
}
#endif
#endif

196
include/telemetry.h Executable file
View File

@ -0,0 +1,196 @@
#ifndef _INCLUDE_TELEMETRY_
#define _INCLUDE_TELEMETRY_
#include <stdint.h>
#include <stddef.h>
#include "comframe.h"
#define TELEMETRY_REQUEST_PAYLOAD 0xFF11
#define TELEMETRY_STATUS_OK 0xAA
#define TELEMETRY_STATUS_ERROR 0x55
#define TELEMETRY_ERROR_TYPE 0x00E1
#define TELEMETRY_ERROR_CHECKSUM 0x00E2
#define TELEMETRY_SEND_COMMAND_INFO_LENGTH 5
#define TELEMETRY_ALARM_TC_A_PRESS 0x00610061
#define TELEMETRY_ALARM_TC_B_PRESS 0x00620062
#define TELEMETRY_ALARM_SUBSYS_PARAM_ERR 0x00630063
#define TELEMETRY_ALARM_WATER_TANK_FULL 0x00640064
#define TELEMETRY_ALARM_N2_PRESS_LOW 0x00650065
#define TELEMETRY_ALARM_O2_PRESS_LOW 0x00660066
#define TELEMETRY_ALARM_RETURN_CAB_FIRE 0x00670067
#define TELEMETRY_ALARM_RETURN_CAB_TOTAL_PRESS 0x00680068
#define TELEMETRY_ALARM_RETURN_CAB_O2_PART_PRESS 0x00690069
#define TELEMETRY_ALARM_RETURN_CAB_CO2_PART_PRESS 0x00700070
#define TELEMETRY_ALARM_INT_PUMP_B_SPEED 0x00710071
#define TELEMETRY_ALARM_CIRC_FAN_SPEED 0x00720072
#define TELEMETRY_ALARM_LND_SEAL_CAB_TOTAL_PRESS 0x00730073
#define TELEMETRY_ALARM_LND_SEAL_CAB_O2_PART_PRESS 0x00740074
#define TELEMETRY_ALARM_LND_SEAL_CAB_CO2_PART_PRESS 0x00750075
#define TELEMETRY_ALARM_LND_SEAL_CAB_FIRE 0x00760076
#define TELEMETRY_ALARM_LND_RAD_LEAK 0x00770077
#define TELEMETRY_ALARM_TAKEOFF 0x00010001
#define TELEMETRY_ALARM_SEP 0x00020002
#define TELEMETRY_ALARM_COMP_DOCK 0x00030003
#define TELEMETRY_ALARM_COMP_SEP 0x00040004
#define TELEMETRY_ALARM_RET_SEP 0x00050005
#define TELEMETRY_ALARM_REC_START 0x00060006
#define TELEMETRY_ALARM_LND 0x00070007
#define TELEMETRY_ALARM_ESC_TOWER_ESC 0x00080008
#define TELEMETRY_ALARM_RESERVED_1 0x00090009
#define TELEMETRY_ALARM_ESC_TOWER_SEP 0x000A000A
#define TELEMETRY_ALARM_VESSEL_ESC 0x00110011
#define TELEMETRY_ALARM_PREP_5_MIN 0x17711771
#define TELEMETRY_ALARM_PREP_1_MIN 0x17721772
#define TELEMETRY_ALARM_DOCK_PREP 0x17731773
#define TELEMETRY_ALARM_DOCK_RING_CONTACT 0x17741774
#define TELEMETRY_ALARM_SEP_COMPLETE 0x17751775
#define TELEMETRY_ALARM_PAYLOAD_SEP 0x17761776
#define TELEMETRY_ALARM_SAILS_DEPLOY 0x17771777
#define TELEMETRY_ALARM_SEP_PREP 0x17781778
#define TELEMETRY_ALARM_SAILS_DEPLOY_PREP 0x17791779
#define TELEMETRY_ALARM_RESERVED_2 0x177A177A
#define TELEMETRY_ALARM_ORBIT_CHG_30_MIN 0x177B177B
#define TELEMETRY_ALARM_ORBIT_CHG_10_MIN 0x177C177C
#define TELEMETRY_ALARM_ORBIT_CHG_5_MIN 0x177D177D
#define TELEMETRY_ALARM_ENGINE_START_CMD 0x177E177E
#define TELEMETRY_ALARM_ENGINE_STOP_CMD 0x177F177F
#define TELEMETRY_ALARM_ORBIT_CHG_END 0x17801780
#define TELEMETRY_ALARM_GNC_SELF_CTRL_MODE 0x17811781
#define TELEMETRY_ALARM_APPROACH_FLIGHT_START 0x17821782
#define TELEMETRY_ALARM_YAW_ADJ_START 0x17831783
#define TELEMETRY_ALARM_YAW_ADJ_COMPLETE 0x17841784
#define TELEMETRY_ALARM_TRANSLATION_PHASE 0x17851785
#define TELEMETRY_ALARM_DOCK_POINT_ENTRY 0x17861786
#define TELEMETRY_ALARM_APPROACH_FLIGHT_CONTINUE 0x17871787
#define TELEMETRY_ALARM_PITCH_ADJ_START 0x17881788
#define TELEMETRY_ALARM_PITCH_ADJ_COMPLETE 0x17891789
#define TELEMETRY_ALARM_FINAL_APPROACH_FLIGHT 0x178A178A
#define ComFrameTelemetry_STATUS_OK(status) ((status) == TELEMETRY_STATUS_OK)
#define ComFrameTelemetry_STATUS_ERR(status) ((status) == TELEMETRY_STATUS_ERROR)
#define ComFrameTelemetry_STATUS_STR(status) ( \
ComFrameTelemetry_STATUS_OK(status) ? "ok" : \
(ComFrameTelemetry_STATUS_ERR(status) ? "error" : "unknown") \
)
#ifdef __cplusplus
extern "C" {
#endif
struct TelemetryCommandInfo {
uint32_t time;
uint8_t code[2];
uint16_t seqid;
} __attribute__((packed));
struct TelemetrySendCommandInfo {
uint8_t seqid;
uint8_t flag;
uint32_t time;
uint8_t code[2];
} __attribute__((packed));
struct TelemetryData4UpperHost { // upper
uint8_t work_status;
uint8_t com_status;
uint8_t coprocessor1_status;
uint8_t coprocessor2_status;
uint8_t voice_circuit_status;
//uint8_t work_status_backup;
uint8_t telemetry_count;
uint8_t voice_mode;
uint8_t recognition_status;
uint8_t recognition_info[8];
uint8_t particle_detection1;
uint8_t particle_detection2;
uint8_t particle_detection3;
// 2024.10.25
struct TelemetryCommandInfo receive_command;
struct TelemetryCommandInfo send_command;
uint8_t volume_key_status;
uint8_t wake_key_status;
uint8_t key_status_backup;
uint8_t current_volume;
uint8_t system_version_high;
uint8_t system_version_low;
uint8_t application_version_high;
uint8_t application_version_low;
uint8_t alarm_code[4];
uint8_t telemetry_backup[5];
} __attribute__((packed));
struct TelemetryData { // telemetry
uint8_t work_status;
uint8_t com_status;
uint8_t coprocessor1_status;
uint8_t coprocessor2_status;
uint8_t voice_circuit_status;
//uint8_t work_status_backup;
uint8_t telemetry_count;
uint8_t voice_mode;
uint8_t recognition_status;
uint8_t recognition_info[8];
uint8_t particle_detection1;
uint8_t particle_detection2;
uint8_t particle_detection3;
// 2024.10.25
struct TelemetryCommandInfo receive_command;
struct TelemetrySendCommandInfo send_command[TELEMETRY_SEND_COMMAND_INFO_LENGTH];
uint8_t volume_key_status;
uint8_t wake_key_status;
uint8_t key_status_backup;
uint8_t current_volume;
uint8_t system_version_high;
uint8_t system_version_low;
uint8_t application_version_high;
uint8_t application_version_low;
uint8_t send_count;
} __attribute__((packed));
static __inline void UpperHostTelemetryData_SwapEndian(struct TelemetryData4UpperHost* tele_data) {
tele_data->receive_command.seqid = byteswaps(tele_data->receive_command.seqid);
tele_data->receive_command.time = byteswapl(tele_data->receive_command.time);
tele_data->send_command.seqid = byteswaps(tele_data->send_command.seqid);
tele_data->send_command.time = byteswapl(tele_data->send_command.time);
}
static __inline void TelemetryData_SwapEndian(struct TelemetryData* tele_data) {
tele_data->receive_command.seqid = byteswaps(tele_data->receive_command.seqid);
tele_data->receive_command.time = byteswapl(tele_data->receive_command.time);
for (size_t i = 0; i < TELEMETRY_SEND_COMMAND_INFO_LENGTH; i++) {
tele_data->send_command[i].time = byteswapl(tele_data->send_command[i].time);
}
}
static __inline ComFrame* NewTelemetryRequestMsg(uint16_t address, const bool swap_endian) {
uint16_t tele_request_payload = (swap_endian) ? byteswaps(TELEMETRY_REQUEST_PAYLOAD) : TELEMETRY_REQUEST_PAYLOAD;
return ComFrame_New(address, COM_FRAME_TYPE_TELEMETRY_REQUEST, &tele_request_payload, sizeof(uint16_t), swap_endian);
}
static __inline ComFrame* NewTelemetryAnswerData(uint16_t address, const struct TelemetryData4UpperHost* tele_data, const bool swap_endian) {
ComFrame* frame = ComFrame_New(address, COM_FRAME_TYPE_TELEMETRY_ANSWER, tele_data, sizeof(struct TelemetryData4UpperHost), swap_endian);
if (swap_endian)
UpperHostTelemetryData_SwapEndian((struct TelemetryData4UpperHost*)(frame->payload));
return frame;
}
static __inline ComFrame* NewTelemetryErrorMsg(uint16_t address, uint16_t code, const bool swap_endian) {
uint16_t tele_error_payload = (swap_endian) ? byteswaps(code) : code;
return ComFrame_New(address, COM_FRAME_TYPE_TELEMETRY_REQUEST, &tele_error_payload, sizeof(uint16_t), swap_endian);
}
ssize_t SendTelemetryErrorMsg(int tty_id, uint16_t address, uint16_t code, const bool swap_endian);
#ifdef __cplusplus
}
#endif
#endif

263
logqueueconfig/CCfgFileParser.cpp Executable file
View File

@ -0,0 +1,263 @@
//bb.cpp
#include <cstdio>
#include <cstring>
#include <fstream>
#include <cerrno>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include "CCfgFileParser.h"
using namespace std;
static inline bool istab(int c) { return (c == '\t'); }
static inline char *strltrim(char *str) {
while (isspace(*str) || istab(*str)) {
++str;
}
return str;
}
static inline char *strrtrim(char *str) {
int len = strlen(str) - 1;
while (isspace(str[len]) || istab(str[len])) {
str[len--] = '\0';
}
return str;
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
int CCfgFileParser::parseFile(const char *lpszFilename) {
using std::string;
std::ifstream in1(lpszFilename);
if (!in1.is_open()) return errno;
char line[2048];
char *pline;
bool bInSection = false;
SECTION_CONTENT *pSC = NULL;
string strSection;
std::pair<string, string> pairKeyValue;
m_error_line = 0;
m_content.clear();
pSC = &m_content["default"];
bInSection = true;
string base64str("");
string mystr("");
while (!in1.eof()) {
in1.getline(line, sizeof(line));
if (line[strlen(line)] != '\n')
mystr += line + string("\n");
else
mystr += line;
}
in1.close();
stringstream in;
in.str(mystr);
while (!in.eof()) { /*if(base64str.size() > sizeof(line))
{
printf("error in config parser!\n");
return -1;
}
memcpy(line, base64str.c_str(), base64str.size());
*/
in.getline(line, sizeof(line));
pline = line;
++m_error_line;
pline = strltrim(pline);
if (pline[0] == '\0') continue; //white line, skip
if (pline[0] == m_chCommentMark) continue; //comment line, skip
if (bInSection) {
//is new-section begin?
if (pline[0] == m_chSectionBMark && extractSection(pline, strSection)) {
pSC = &m_content[strSection];
}
else if (extractKeyValue(pline, pairKeyValue)) {
//key-value pair
pSC->insert(pairKeyValue);
}
else {
//in.close();
return SYNTAX_ERROR;
}
}
else { //NOT in section
//is a valid section?
if (extractSection(pline, strSection)) {
pSC = &m_content[strSection];
bInSection = true;
}
else {
//in.close();
return SYNTAX_ERROR;
}
}
}
// in.close();
return 0;
}
bool CCfgFileParser::getValue(const std::string &strSection,
const std::string &strKey, const char * &Value) const {
FILE_CONTENT::const_iterator it;
if ((it = m_content.find(strSection)) != m_content.end()) {
SECTION_CONTENT::const_iterator p;
const SECTION_CONTENT &section = (*it).second;//m_content[strSection];
if ((p = section.find(strKey)) != section.end()) {
Value = ((*p).second).c_str();
return true;
}
}
if (m_bShowError)
fprintf(stderr, "***Error: fail to read [%s]:%s\n", strSection.c_str(), strKey.c_str());
return false;
}
bool CCfgFileParser::getValue(const std::string &strKey, const char * &pValue) const {
return getValue("default", strKey, pValue);
}
bool CCfgFileParser::getIntValue(const std::string &strSection,
const std::string& strKey, long& Value) const {
const char* pstr;
if (getValue(strSection, strKey, pstr) && strlen(pstr) > 0)
{
if (strlen(pstr) > 0)
{
Value = atol(pstr);
return true;
}
}
if (m_bShowError)
fprintf(stderr, "***Error: fail to read [%s]:%s\n", strSection.c_str(), strKey.c_str());
return false;
}
bool CCfgFileParser::getIntValue(const std::string &strKey, long &Value) const {
return getIntValue("default", strKey, Value);
}
bool CCfgFileParser::getDoubleValue(const std::string &strSection,
const std::string &strKey, double &Value) const {
const char *pstr;
if (getValue(strSection, strKey, pstr) && strlen(pstr) > 0)
{
Value = atof(pstr);
return true;
}
else
{
if (m_bShowError)
fprintf(stderr, "***Error: fail to read [%s]:%s\n", strSection.c_str(), strKey.c_str());
return false;
}
}
bool CCfgFileParser::getDoubleValue(const std::string &strKey, double &Value) const {
return getDoubleValue("default", strKey, Value);
}
/*
* Description: Extract section name
* Parameters: line[IN]---A string line to be parsed
* strSection[OUT]---Section name
*
* Return Value: if section name is in the line return true,or return false
*/
bool CCfgFileParser::extractSection(char *line, std::string &strSection) {
char *tmp;
if (line[0] == m_chSectionBMark) {
if ((tmp = strchr(++line, m_chSectionEMark)) != NULL) {
*tmp = '\0';
strSection = line;
return true;
}
}
return false;
}
/*
* Description: Parse a record line into a std:pair as key and value
* Parameters: line[IN]---A string to be parsed
* pairKeyValue[OUT]---Parsing result
*
* Return Value: If parse successfully return true,or return false
*/
bool CCfgFileParser::extractKeyValue(char *line,
std::pair<std::string, std::string> &pairKeyValue) {
char *tmp;
if ((tmp = strchr(line, m_chRecordEMark)) != NULL || (tmp = strchr(line, '\r')) != NULL ||
(tmp = strchr(line, '='))) {
if (*tmp == '=')
tmp = line + strlen(line); // tmp++;
*tmp = '\0'; //ignore content after ';'(the RecordEMark)
if ((tmp = strchr(line, '=')) != NULL) {
*tmp++ = '\0';
tmp = strltrim(tmp);
tmp = strrtrim(tmp);
line = strrtrim(line);
pairKeyValue.first = line;
pairKeyValue.second = tmp;
return true;
}
}
return false;
}
const char *CCfgFileParser::getErrorString(int err) {
static char buf[100];
if (err == SYNTAX_ERROR) {
sprintf(buf, "configuration file format is invalid at line %d", m_error_line);
return buf;
}
else {
return strerror(err);
}
}
void CCfgFileParser::printContent() {
using std::cout;
using std::endl;
FILE_CONTENT::const_iterator pf;
SECTION_CONTENT::const_iterator ps;
for (pf = m_content.begin(); pf != m_content.end(); ++pf) {
cout << "section:" << (*pf).first << endl;
const SECTION_CONTENT &sc = (*pf).second;
for (ps = sc.begin(); ps != sc.end(); ++ps) {
cout << '\t' << (*ps).first << "=" << (*ps).second << endl;
}
}
}
// 在库中定义一个工厂函数 createParser
extern "C" __attribute__((visibility("default"))) CCfgFileParser* createParser() {
return new CCfgFileParser(); // 在工厂函数中调用构造函数创建对象并返回指针
}
// 在库中定义一个销毁函数 deleteParser
extern "C" __attribute__((visibility("default"))) void deleteParser(CCfgFileParser* parser) {
// 在销毁函数中调用析构函数销毁对象
delete parser;
}

78
logqueueconfig/CCfgFileParser.h Executable file
View File

@ -0,0 +1,78 @@
#if !defined(AFX_CFGFILEPARSERGENERATOR_H__AAF8F913_7694_4A69_A05F_57A1A1887CA1__INCLUDED_)
#define AFX_CFGFILEPARSERGENERATOR_H__AAF8F913_7694_4A69_A05F_57A1A1887CA1__INCLUDED_
#include <stdlib.h>
#include <stdio.h>
#ifdef WIN32
#pragma warning(disable:4786)
#pragma warning(disable:4503)
#endif
#include <map>
#include <string>
class __attribute__((visibility("default"))) CCfgFileParser
{
public:
explicit CCfgFileParser(bool base64 = false,
char chSectionBMark = '[',
char chSectionEMark = ']',
char chRecordEMark = ';', //record end-mark
char chCommentMark = '#'
)
:m_base64(base64), m_chSectionBMark(chSectionBMark), m_chSectionEMark(chSectionEMark),
m_chRecordEMark(chRecordEMark), m_chCommentMark(chCommentMark), m_bShowError(false)
{
}
virtual ~CCfgFileParser() {}
typedef std::map<std::string, std::string> SECTION_CONTENT;
typedef std::map<std::string, SECTION_CONTENT > FILE_CONTENT;
private:
bool m_base64;
enum{
SYNTAX_ERROR = 0x100,
};
public:
bool m_bShowError;
const char* getErrorString(int err);
int parseFile(const char* lpszFilename);
int sections() const { return m_content.size(); }
FILE_CONTENT::const_iterator sectionBegin() const { return m_content.begin(); }
FILE_CONTENT::const_iterator sectionEnd() const { return m_content.end(); }
//return empty-string if no corresponding value presented.
bool getValue(const std::string& strSection, const std::string& strKey, const char *&Value) const;
bool getValue(const std::string& strKey, const char *&Value) const;
bool getIntValue(const std::string& strSection, const std::string& strKey, long &pValue) const;
bool getIntValue(const std::string& strKey, long &Value) const;
bool getDoubleValue(const std::string& strSection, const std::string& strKey, double &Value) const;
bool getDoubleValue(const std::string& strKey, double &Value) const;
void printContent();
protected:
bool extractSection(char* line, std::string& strSection);
bool extractKeyValue(char* line, std::pair<std::string, std::string>& pairKeyValue);
private:
FILE_CONTENT m_content;
char m_chSectionBMark;
char m_chSectionEMark;
char m_chRecordEMark;
char m_chCommentMark;
int m_error_line;
};
extern "C" __attribute__((visibility("default"))) CCfgFileParser* createParser();
extern "C" __attribute__((visibility("default"))) void deleteParser(CCfgFileParser* parser);
#endif // !defined(AFX_CFGFILEPARSERGENERATOR_H__AAF8F913_7694_4A69_A05F_57A1A1887CA1__INCLUDED_)

44
logqueueconfig/Makefile Executable file
View File

@ -0,0 +1,44 @@
INCS = log.h CCfgFileParser.h
SRCS = CCfgFileParser.cpp log.cpp dataqueue.cpp
CXX = g++
AR = ar
EXEC_SO = libqueuelog.so
EXEC_A = libqueuelog.a
EXEC_O = libqueuelog.o
# 输出目录
OUTPUT_LIB_DIR = ../../libs/
OUTPUT_INCLUDE_DIR=../../includes/
ifdef DEBUG_VERSION
CFLAGS := -DMY_DEBUG -fvisibility=hidden -g -Wall -fPIC -shared -std=c++11 -Wl,--as-needed
else
CFLAGS := -fvisibility=hidden -g3 -O2 -Wall -fPIC -shared -std=c++11 -Wl,--as-needed
endif
LFLAGS1 = -lpthread
OBJS = $(SRCS:.cpp=.o)
all: $(EXEC_SO) $(EXEC_A)
$(EXEC_SO): $(OBJS)
$(CXX) -o $(EXEC_SO) $(OBJS) $(CFLAGS) $(LFLAGS1)
$(EXEC_A): $(OBJS)
$(AR) rcs $(EXEC_A) $(OBJS)
mkdir -p $(OUTPUT_LIB_DIR)
mkdir -p $(OUTPUT_INCLUDE_DIR)
mv $(EXEC_A) $(OUTPUT_LIB_DIR)
mv $(EXEC_SO) $(OUTPUT_LIB_DIR)
cp -u *.h $(OUTPUT_INCLUDE_DIR)
find . -name "*.d" -type f -print -exec rm -rf {} \;
@echo "Compile successfully! result-->" $(OUTPUT_LIB_DIR)
%.o: %.cpp
$(CXX) -o $@ -c $< $(CFLAGS)
.PHONY: clean
clean:
rm -f $(EXEC_SO) $(EXEC_A) $(EXEC_O) $(OBJS)

11
logqueueconfig/backup Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
#rm -f *.so *.o
CUR=$(basename `pwd`)
echo $CUR
mkdir -p ../../_backup
tar --exclude=*.so --exclude=*.a --exclude=*.o --exclude=*.d --exclude=./.vs --exclude=*.git* --exclude=*.swp -czvf ../../_backup/${CUR}-`date +"%Y.%m.%d-%H.%M"`.tar.gz ../${CUR}/*
#tar -czvf ../_backup/${CUR}-`date +"%Y.%m.%d-%H.%M"`.tar.gz ../${CUR}
curDate=$(date +"%Y.%m.%d %H:%M:%S")
echo backup done@${curDate}

537
logqueueconfig/dataqueue.cpp Executable file
View File

@ -0,0 +1,537 @@
//#pragma once
#include <time.h>
#include "dataqueue.h"
#include <stdio.h>
#define BLOCK_PADDING_SIZE 64 //bytes
#define HEADER_BYTES 16//remain: block-bytes: 8 bytes int64_t + data bytes: 8bytes int64_t
#define CHECK_QUEUE_ERR 1
#define myMax(x,y) ((x)>(y)?(x):(y))
typedef void* voidPtr;
static char g_strDQCurTime[50] = "";
//----------------------------------------------------------------------------------------
inline char* GetCurTimeStr(char* strBuf, int iBufSize)// g_strDQCurTime
{
time_t tt;
tt = time(NULL);
strftime(strBuf, iBufSize, "%Y.%m.%d %H:%M:%S", localtime(&tt));//gener string with format:"YYYY-MM-DD hh:mm:ss"
return strBuf;
}
#define DATA_QUEUE_RETAIN_BLOCK_CNT 4
//----------------------------------------------------------------------------------------
DataQueue::DataQueue(int64_t iSizeInKB, int64_t iMaxBlockSize, bool bUseMutex, int iReadThreadCnt)
{
m_pDataBuf = NULL;
m_bUseMutex = bUseMutex;
if (m_bUseMutex)
pthread_mutex_init(&m_BufMutex, NULL);
InitQueue(iSizeInKB, iMaxBlockSize, iReadThreadCnt);//KB
}
//----------------------------------------------------------------------------------------
DataQueue::~DataQueue()
{
if (m_pDataBuf != NULL)
free((void*)m_pDataBuf);
if (m_bUseMutex)
pthread_mutex_destroy(&m_BufMutex);
}
//----------------------------------------------------------------------------------------
int DataQueue::InitQueue(int64_t iTotalKB4QueueBuf, int iMaxBlockBytes, int iReadThreadCnt)//KB
{
if (m_pDataBuf != NULL)
free((void*)m_pDataBuf);
m_pDataBuf = NULL;
m_iMaxBlockByte = iMaxBlockBytes;
m_iReservedBlockNum = iReadThreadCnt + DATA_QUEUE_RETAIN_BLOCK_CNT;
m_iReservedBufBytes = m_iMaxBlockByte * m_iReservedBlockNum; //4 times of m_iMaxBlockByte
int64_t iTotalByes = 0;
if (iTotalKB4QueueBuf > 0)
{
iTotalByes = iTotalKB4QueueBuf * 1024 + 2 * HEADER_BYTES + m_iReservedBufBytes; //KB;
m_pDataBuf = (uint8_t*)malloc(iTotalByes); //for safe sake in case of over step the border
if (m_pDataBuf == NULL)
{
fprintf(stderr, "\n----------------------------------------------------------------\n");
GetCurTimeStr(g_strDQCurTime, sizeof(g_strDQCurTime));
fprintf(stderr, "***Error: fail to malloc for data queue, mem:%ld @line:%d %s\n", iTotalKB4QueueBuf, __LINE__, g_strDQCurTime);
fprintf(stderr, "----------------------------------------------------------------\n");
fflush(stderr);
exit(4);
}
}
m_iFreeBytes = m_iQueueBufBytes = iTotalByes;
ResetDataQueue();
return 0;
}
//----------------------------------------------------------------------------------------
void DataQueue::ResetDataQueue()
{
if (m_bUseMutex)
pthread_mutex_lock(&m_BufMutex);
m_iBufOutPos = m_iBufInPos = 0;
m_iRcvBlocks = 0;
m_iRcvBytes = 0;
m_iProcBlocks = 0;
m_iEmptyTimes = 0;
m_iFullTimes = 0;
m_iPaddingBlocks = m_iProcPaddingBlocks = 0;
m_iFreeBytes = m_iQueueBufBytes;
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
}
//----------------------------------------------------------------------------------------
int64_t DataQueue::GetBufBytes()
{//Get buf size
int64_t iBytes;
iBytes = m_iQueueBufBytes - m_iReservedBufBytes;
if (iBytes < 0)
iBytes = 0;
return iBytes;
}
//----------------------------------------------------------------------------------------
int64_t DataQueue::GetQueueFreeBytes()
{
//int64_t iFreeBytes = (m_iBufInPos >= m_iBufOutPos ? (m_iQueueBufBytes - (m_iBufInPos - m_iBufOutPos)) : ((m_iBufOutPos - m_iBufInPos) ) );
int64_t iFreeBytes;
iFreeBytes = (m_iBufInPos >= m_iBufOutPos ? (m_iQueueBufBytes - (m_iBufInPos - m_iBufOutPos)) : ((m_iBufOutPos - m_iBufInPos)));
iFreeBytes -= m_iReservedBufBytes;
if (iFreeBytes < 0)//iFreeBytes never is 0 because of m_iReservedBufBytes, unless the beginning state where m_iBufOutPos and m_iBufInPos are same
iFreeBytes = 0;
return iFreeBytes;
}
//----------------------------------------------------------------------------------------
int64_t DataQueue::GetFreeBytes()
{
return GetQueueFreeBytes();
}
//----------------------------------------------------------------------------------------
inline bool DataQueue::CanRcv(int iNeedBytes)
{
bool bCanRcv;
m_iMaxBlockByte = iNeedBytes > m_iMaxBlockByte ? iNeedBytes : m_iMaxBlockByte;
m_iReservedBufBytes = m_iMaxBlockByte * m_iReservedBlockNum; //4<><34><EFBFBD><EFBFBD>m_iMaxBlockByte
bCanRcv = (GetQueueFreeBytes() >= iNeedBytes);
return bCanRcv;
}
//----------------------------------------------------------------------------------------
bool DataQueue::IsFull()
{
//---- 这种判断方式可能存在错误期间m_iBufOutPos到尾部折回到队列开头变得小于m_iBufInPos
//int iFreeBytes = (m_iBufInPos < m_iBufOutPos ? (m_iBufOutPos - m_iBufInPos) : (m_iQueueBufBytes - (m_iBufInPos - m_iBufOutPos)));//
bool bFull;
int64_t iFreeBytes;
iFreeBytes = (m_iBufInPos >= m_iBufOutPos ? (m_iQueueBufBytes - (m_iBufInPos - m_iBufOutPos)) : ((m_iBufOutPos - m_iBufInPos)));
bFull = (iFreeBytes <= m_iReservedBufBytes);
// if( bFull )
// printf("dataqueue is full, size:%ld free:%ld - %ld < retain:%ld in:%ld out:%ld\n",
// m_iQueueBufBytes,iFreeBytes, GetFreeBytes(),m_iReservedBufBytes,m_iBufInPos,m_iBufOutPos );
return bFull;
}
//----------------------------------------------------------------------------------------
bool DataQueue::IsEmpty()
{
return (m_iBufInPos == m_iBufOutPos);
}
//----------------------------------------------------------------------------------------
int64_t DataQueue::EnQueue(void* e, int64_t iDataBytes, void* extradata, int64_t iExtraDataBytes, void* extradata2, int64_t iExtraDataBytes2)
{ // >=0 for normal & success,
//<0: error
int64_t iBlockBytes;
void* pWritePos = NULL;
int64_t iPadding = 0;
if (iDataBytes < 0 || iExtraDataBytes < 0 || iExtraDataBytes2 < 0)
{
fprintf(stderr, "iDataBytes %ld iExtraDataBytes %ld iExtraDataBytes %ld\n",
iDataBytes, iExtraDataBytes, iExtraDataBytes2);
fflush(stderr);
throw __LINE__;
}
if (m_bUseMutex)
pthread_mutex_lock(&m_BufMutex);
int64_t iTotalDataBytes = iDataBytes + iExtraDataBytes + iExtraDataBytes2;
#if CHECK_QUEUE_ERR
CheckQueue(__LINE__, 0);
#endif
iBlockBytes = (((iTotalDataBytes + 7) >> 3) << 3)//8<>ı<EFBFBD><C4B1><EFBFBD>
+ HEADER_BYTES
+ BLOCK_PADDING_SIZE;// 32
if (!CanRcv(iBlockBytes))
{
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
return QUEUE_IS_FULL;
}
if (m_iBufInPos >= m_iBufOutPos)
{
if (m_iBufInPos + iBlockBytes > m_iQueueBufBytes)
{//create a padding block
*((int64_t*)(m_pDataBuf + m_iBufInPos)) = m_iQueueBufBytes - m_iBufInPos; //m_iQueueBufBytes: the next block is at the begining of the buf
*((int64_t*)(m_pDataBuf + m_iBufInPos + sizeof(int64_t))) = 0;
//m_iRcvBlocks++;
//printf("+iBlockBytes:%d, iDataByte:%d\n", m_iQueueBufBytes - m_iBufInPos, 0);
m_iPaddingBlocks++;
iPadding++;
m_iBufInPos = 0;
}
}
#if CHECK_QUEUE_ERR
CheckQueue(__LINE__, iBlockBytes);
#endif
pWritePos = m_pDataBuf + m_iBufInPos;
*((int64_t*)pWritePos) = iBlockBytes;
*((int64_t*)(pWritePos + sizeof(int64_t))) = iTotalDataBytes;
memcpy(pWritePos + HEADER_BYTES, e, iDataBytes);
if (extradata && iExtraDataBytes > 0)
memcpy(pWritePos + HEADER_BYTES + iDataBytes, extradata, iExtraDataBytes);//iExtraDataBytes
if (extradata2 && iExtraDataBytes2 > 0)
memcpy(pWritePos + HEADER_BYTES + iDataBytes + iExtraDataBytes, extradata2, iExtraDataBytes2);//iExtraDataBytes
m_iRcvBlocks++;
m_iRcvBytes += iTotalDataBytes;
m_iBufInPos = (m_iBufInPos + iBlockBytes) % m_iQueueBufBytes;
//#ifdef MY_DEBUG
// printf("+iBlockBytes:%d, iDataByte:%d\n", iBlockBytes, iDataBytes);
//#endif
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
return iTotalDataBytes;
}
//----------------------------------------------------------------------------------------
#ifdef ARM_VERSION //NO_PORN_FUNC
#define THERE_ARE_CONCURRENT_DEQUEUE_OP 1
#else
#define THERE_ARE_CONCURRENT_DEQUEUE_OP 1
#endif
int64_t DataQueue::DeQueue(void** e)
{//return value: Bytes, >0 if sucess, =0 no data, < 0 error. No data copy, only return the pointer of data area
void* p;
int iBlockBytes, iDataBytes;
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
{
pthread_mutex_lock(&m_BufMutex);
#if CHECK_QUEUE_ERR
CheckQueue(__LINE__, 0);
#endif
}
#endif
if (IsEmpty())
{
//#ifdef MY_DEBUG
// m_iEmptyTimes++;
// if ( m_iEmptyTimes <5 || ( m_iEmptyTimes % 50 == 0) )
// {
// printf("***Block Queue IsEmpty %1dth: m_iBufInPos=%lu, m_iBufOutPos=%lu, m_iQueueBufBytes=%lu, m_iReservedBufBytes=%lu\n",
// m_iEmptyTimes, m_iBufInPos, m_iBufOutPos, m_iQueueBufBytes, m_iReservedBufBytes); //<2F>ճ<EFBFBD>4<EFBFBD><34>
// fflush(stdout);
// }
//#endif
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
#endif
return EMPTY_QUEUE_NO_DATA;
}
p = m_pDataBuf + m_iBufOutPos;
iBlockBytes = *((int64_t*)p);
iDataBytes = *((int64_t*)(p + sizeof(int64_t)));
//int iDataBytes = *((int*)(p + sizeof(int)));
m_iBufOutPos = (m_iBufOutPos + iBlockBytes) % m_iQueueBufBytes;
//#ifdef MY_DEBUG
// printf("-iBlockBytes:%d, iDataByte:%d\n", iBlockBytes, iDataBytes);
//#endif
if (iDataBytes > 0)//not pading block
{
m_iProcBlocks++;
*e = (void*)(p + HEADER_BYTES);
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
#endif
return iDataBytes;
}
else//padding block
{
m_iProcPaddingBlocks++;
if (IsEmpty())
{
//printf("CSegmentPool IsEmpty\n");
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
#endif
return EMPTY_QUEUE_NO_DATA;
}
p = m_pDataBuf + m_iBufOutPos;
iBlockBytes = *((int64_t*)p);
iDataBytes = *((int64_t*)(p + sizeof(int64_t)));
//#ifdef MY_DEBUG
// printf("-iBlockBytes:%d, iDataByte:%d\n", iBlockBytes, iDataBytes);
//#endif
m_iBufOutPos = (m_iBufOutPos + iBlockBytes) % m_iQueueBufBytes;
#if CHECK_QUEUE_ERR
if (m_bUseMutex)
CheckQueue(__LINE__, 0);
#endif
if (iDataBytes > 0)//not pading block
{
m_iProcBlocks++;
*e = (void*)(p + HEADER_BYTES);
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
#endif
return iDataBytes;
}
else
{//pading block
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
#endif
m_iProcPaddingBlocks++;
char strMsg[200];
GetCurTimeStr(g_strDQCurTime, sizeof(g_strDQCurTime));
sprintf(strMsg, "***Error in DeQueue: 2 successive padding block!@line %d %s\n", __LINE__, g_strDQCurTime);
fprintf(stderr, "\n----------------------------------------------------------------\n");
fprintf(stderr, "%s", strMsg);
fprintf(stderr, "----------------------------------------------------------------\n");
fflush(stderr);
exit(0);
return EMPTY_QUEUE_NO_DATA;
}
}
#if THERE_ARE_CONCURRENT_DEQUEUE_OP
if (m_bUseMutex)
pthread_mutex_unlock(&m_BufMutex);
#endif
char strMsg[200];
GetCurTimeStr(g_strDQCurTime, sizeof(g_strDQCurTime));
sprintf(strMsg, "***Error in DeQueue: @line %d %s\n", __LINE__, g_strDQCurTime);
fprintf(stderr, "\n----------------------------------------------------------------\n");
fprintf(stderr, "%s", strMsg);
fprintf(stderr, "----------------------------------------------------------------\n");
fflush(stderr);
exit(0);
return EMPTY_QUEUE_NO_DATA;
}
//----------------------------------------------------------------------------------------
void DataQueue::OutPutStatus(char* strBuf, int iBufLen)
{
int iLen = iBufLen, iStrLen;
char* p = strBuf;
snprintf(p, iLen, "m_iBufOutPos = % ld, m_iBufInPos = % ld, m_iFreeBytes = % ld, m_iQueueBufBytes = % ld, m_iRcvBlocks = % ld, m_iRcvBytes = % ld\n",
m_iBufOutPos, m_iBufInPos, m_iFreeBytes, m_iQueueBufBytes, m_iRcvBlocks, m_iRcvBytes);
iStrLen = strlen(p);
p += iStrLen;
iLen -= iStrLen;
snprintf(p, iLen, "\m_iProcBlocks=%ld, m_iPaddingBlocks=%ld, m_iProcPaddingBlocks=%ld, m_iMaxBlockByte=%ld, m_iReservedBufBytes=%ld, m_iReservedBlockNum=%ld,m_iEmptyTimes=%ld, m_iFullTimes=%ld\n",
m_iProcBlocks, m_iPaddingBlocks, m_iProcPaddingBlocks, m_iMaxBlockByte, m_iReservedBufBytes, m_iReservedBlockNum, m_iEmptyTimes, m_iFullTimes);
iStrLen = strlen(p);
p += iStrLen;
iLen -= iStrLen;
}
//----------------------------------------------------------------------------------------
bool DataQueue::CheckQueue(int iLineNo, int iNeedBytes)
{
int64_t iFreeBytes;
int iErr = 0;
//int iFreeBytes = (m_iBufInPos < m_iBufOutPos ? (m_iBufOutPos - m_iBufInPos) : (m_iQueueBufBytes - (m_iBufInPos - m_iBufOutPos)) );//
iFreeBytes = (m_iBufInPos >= m_iBufOutPos ? (m_iQueueBufBytes - (m_iBufInPos - m_iBufOutPos)) : (m_iBufOutPos - m_iBufInPos));
if (m_iBufOutPos < 0)
iErr |= 0x1;
if (m_iBufInPos < 0)
iErr |= 0x2;
if (iFreeBytes < (m_iReservedBlockNum - 2) * m_iMaxBlockByte)
iErr |= 0x4;
if (iFreeBytes < iNeedBytes)
iErr |= 0x8;
if (m_bUseMutex && (m_iProcBlocks == m_iRcvBlocks)
&& (m_iPaddingBlocks == m_iProcPaddingBlocks)
&& (m_iBufOutPos != m_iBufInPos))
iErr |= 0x10;
if (iErr)
{
char strMsg[1000];
char strInfo[1000];
OutPutStatus(strMsg, sizeof(strMsg));
snprintf(strInfo, sizeof(strInfo), "******QueueError %d @line %d, iFreeBytes=%ld , iNeedBytes=%d:\n%s \n",
iErr, iLineNo, iFreeBytes, iNeedBytes, strMsg);
FILE* fp = fopen("err.log", "a+");
if (fp)
{
fprintf(fp, "%s\n", strInfo);
fflush(fp);
fclose(fp);
}
fprintf(stderr, "\n----------------------------------------------------------------\n");
fprintf(stderr, "%s\n", strInfo);
fprintf(stderr, "----------------------------------------------------------------\n");
fflush(stderr);
//throw __LINE__;
return false;
}
return true;
}
//----------------------------------------------------------------------------------------
extern "C" voidPtr __attribute__((visibility("default"))) OpenDataQueue(int64_t iTotalKB4QueueBuf, int iMaxBlockBytes, int iReadThreadCnt)
{
//DataQueue* p = new DataQueue(iTotalKB4QueueBuf, iMaxBlockBytes, bConcurrent);//bUseMutex = bConcurrent
DataQueue* p = new DataQueue(iTotalKB4QueueBuf, iMaxBlockBytes, true);//bUseMutex = true
return (void*)p;
}
//----------------------------------------------------------------------------------------
extern "C" bool __attribute__((visibility("default"))) GetDataQueueInfo(void* pQueue, int64_t iSize[5])
{//return the total data blocks, and set the buf with info string
int64_t iFreeBytes, iBufBytes;
DataQueue* p = (DataQueue*)pQueue;
if (!pQueue)
return false;
iSize[0] = p->GetBufBytes();
iSize[1] = p->GetFreeBytes();
iSize[2] = p->GetTotalRcvBlockCnt();
iSize[3] = p->GetProcBlockCnt();
iSize[4] = p->GetBlockCnt();
return true;
}
//----------------------------------------------------------------------------------------
#define LOG_QUEUE_INFO 0
extern "C" int64_t __attribute__((visibility("default"))) EnDataQueue(void* pQueue, void* e, int64_t iDataBytes, void* extradata1, int64_t iExtraDataBytes1, void* extradata2, int64_t iExtraDataBytes2)
{// int DataQueue::EnQueue(void* e, int64_t iDataBytes, void* extradata1, int64_t iExtraDataBytes1, void* extradata2, int64_t iExtraDataBytes2)
int64_t iRet =0;
if (pQueue)
{
#if LOG_QUEUE_INFO
char strBuf[500];
char strBuf2[500];
FILE* fp = NULL;
snprintf(strBuf, sizeof(strBuf), "queue%p.log", pQueue);
fp = fopen(strBuf, "a+");
if (fp)
{
snprintf(strBuf, sizeof(strBuf), "EnQueue1: iDataBytes=%ld, extradata1=%ld, extradata2=%ld\n",
iDataBytes, iExtraDataBytes1, iExtraDataBytes2);
((DataQueue*)pQueue)->OutPutStatus(strBuf2, sizeof(strBuf2));
fprintf(fp, "%s%s", strBuf, strBuf2);
}
#endif
iRet = ((DataQueue*)pQueue)->EnQueue(e, iDataBytes, extradata1, iExtraDataBytes1, extradata2, iExtraDataBytes2);
#if LOG_QUEUE_INFO
if (fp)
{
((DataQueue*)pQueue)->OutPutStatus(strBuf2, sizeof(strBuf2));
fprintf(fp, "EnQueue2: iRet=%ld\n%s\n\n", iRet, strBuf2);
fclose(fp);
}
#endif
}
return iRet;
}
extern "C" int64_t __attribute__((visibility("default"))) DeDataQueue(void* pQueue, void** e)
{
int64_t iRet = 0;
if (pQueue)
{
#if LOG_QUEUE_INFO
char strBuf[500];
char strBuf2[500];
FILE* fp = NULL;
snprintf(strBuf, sizeof(strBuf), "queue%p.log", pQueue);
fp = fopen(strBuf, "a+");
if (fp)
{
((DataQueue*)pQueue)->OutPutStatus(strBuf2, sizeof(strBuf2));
fprintf(fp, "DeQueue1: %s\n", strBuf2);
}
#endif
iRet = ((DataQueue*)pQueue)->DeQueue(e);
#if LOG_QUEUE_INFO
if (fp)
{
((DataQueue*)pQueue)->OutPutStatus(strBuf2, sizeof(strBuf2));
fprintf(fp, "DeQueue2: iRet=%ld\n%s\n\n", iRet, strBuf2);
fclose(fp);
}
#endif
}
return iRet;
}
extern "C" bool __attribute__((visibility("default"))) DataQueueIsFull(void* pQueue)
{
return ((DataQueue*)pQueue)->IsFull();
}
extern "C" void __attribute__((visibility("default"))) ResetDataQueue(void* pQueue)
{//delete all data in the queue, and reset all counters to 0
((DataQueue*)pQueue)->ResetDataQueue();
}
extern "C" void __attribute__((visibility("default"))) CloseDataQueue(void*& pQueue)
{
if (pQueue)
delete (DataQueue*)pQueue;
pQueue = NULL;
}

78
logqueueconfig/dataqueue.h Executable file
View File

@ -0,0 +1,78 @@
//#pragma once
#ifndef __HIT_DATAQUEUE_H
#define __HIT_DATAQUEUE_H
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <cstring>
#define MAX_BLOCK_BYTES (1024*1024*2)
#define QUEUE_IS_FULL (-1)
#define EMPTY_QUEUE_NO_DATA (0)
class DataQueue
{
public:
DataQueue(int64_t iSizeInKB = 0, int64_t iMaxBlockSize = MAX_BLOCK_BYTES, bool bUseMutex = true, int iReadThreadCnt=1);
~DataQueue();
//private:
void* m_pDataBuf; //[DATA_BUF_LEN]
int64_t m_iBufOutPos;
int64_t m_iBufInPos;
int64_t m_iFreeBytes;
int64_t m_iQueueBufBytes;
int64_t m_iRcvBlocks;//total count
int64_t m_iRcvBytes;//total Bytes
int64_t m_iProcBlocks;
int64_t m_iPaddingBlocks;//padding block cnt in enqueue
int64_t m_iProcPaddingBlocks;//processed padding block cnt in dequeue
int64_t m_iMaxBlockByte;
int64_t m_iReservedBufBytes;
int64_t m_iReservedBlockNum;
int64_t m_iEmptyTimes;
int64_t m_iFullTimes;
bool m_bUseMutex;
pthread_mutex_t m_BufMutex;//pthread_mutex_init(&m_BufMutex, NULL);
bool IsEmpty(); //
bool IsFull();
inline bool CanRcv(int iNeedBytes);
public:
int64_t EnQueue(void* pdata, int64_t iDataBytes = 0, void* extradata = NULL, int64_t iExtraDataBytes = 0, void* extradata2 = NULL, int64_t iExtraDataBytes2 = 0);
int64_t DeQueue(void** pdata);//return value: Bytes, >0 if sucess, =0 no data, < 0 error. No data copy, only return the pointer of data area
uint64_t GetBlockCnt() {
return m_iRcvBlocks - m_iProcBlocks;
};
int64_t GetProcBlockCnt() {
return m_iProcBlocks;
};
int64_t GetTotalRcvBlockCnt() {
return m_iRcvBlocks;
};
int64_t GetFreeBytes();
int64_t GetBufBytes();
inline int64_t GetQueueFreeBytes();
int InitQueue(int64_t iTotalKB4QueueBuf, int iMaxBlockKB = MAX_BLOCK_BYTES, int iReadThreadCnt = 1);//KB
bool CheckQueue(int iLineNo, int iNeedBytes);
void OutPutStatus(char* strBuf, int iLen);
void ResetDataQueue();
};
#define LOG_QUEUE_INFO 0
typedef void* voidPtr;
extern "C" voidPtr __attribute__((visibility("default"))) OpenDataQueue(int64_t iTotalKB4QueueBuf, int iMaxBlockBytes, int iReadThreadCnt);
extern "C" bool __attribute__((visibility("default"))) GetDataQueueInfo(void* pQueue, int64_t iSize[5]);
extern "C" int64_t __attribute__((visibility("default"))) EnDataQueue(void* pQueue, void* e, int64_t iDataBytes, void* extradata1, int64_t iExtraDataBytes1, void* extradata2, int64_t iExtraDataBytes2);
extern "C" int64_t __attribute__((visibility("default"))) DeDataQueue(void* pQueue, void** e);
extern "C" bool __attribute__((visibility("default"))) DataQueueIsFull(void* pQueue);
extern "C" void __attribute__((visibility("default"))) ResetDataQueue(void* pQueue);
extern "C" void __attribute__((visibility("default"))) CloseDataQueue(void*& pQueue);
#endif

42
logqueueconfig/log.cpp Executable file
View File

@ -0,0 +1,42 @@
#include "log.h"
namespace QUEUE_LOG_COFIG
{
void* WriteLogThread(void* param)
{
CLogFile* pLog = (CLogFile*)(param);
pLog->writeQueue2File();//
}
}
using namespace QUEUE_LOG_COFIG;
typedef void* voidPtr;
//--------------------------------------------------------------------------------------------------------------
extern "C" voidPtr __attribute__((visibility("default"))) OpenLogFile(const char* strPath, const char* strFileName, bool bLogByDay)
{
//CLogFile* p = new CLogFile(strPath, strFileName, bLogByDay, 1);//use queue
CLogFile* p = new CLogFile(strPath, strFileName, false, 1);//use queue 2021.11.09
return (voidPtr)p;
}
//-----------------------------------------------------------------------------------------------------------------
extern "C" int __attribute__((visibility("default"))) WriteLog(void* pLogFile, const char* fmt, ...)
{
if (!pLogFile)
return -1;
char strInfo[INFO_BUF_LEN];
va_list arglist;
va_start(arglist, fmt);
vsprintf(strInfo, fmt, arglist);
va_end(arglist);
((CLogFile*)pLogFile)->append_one_item(0, strInfo);
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
extern "C" void __attribute__((visibility("default"))) CloseLog(void* pLogFile)
{
((CLogFile*)pLogFile)->close_log_file();
return;
}

287
logqueueconfig/log.h Executable file
View File

@ -0,0 +1,287 @@
#ifndef __HIT_LOG_HEADER
#define __HIT_LOG_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include "dataqueue.h"
#define INFO_BUF_LEN 4096//2048
typedef void* voidPtr;
extern "C" voidPtr __attribute__((visibility("default"))) OpenLogFile(const char* strPath, const char* strFileName, bool bLogByDay);
//-----------------------------------------------------------------------------------------------------------------
extern "C" int __attribute__((visibility("default"))) WriteLog(void* pLogFile, const char* fmt, ...);
//-----------------------------------------------------------------------------------------------------------------
extern "C" void __attribute__((visibility("default"))) CloseLog(void* pLogFile);
namespace QUEUE_LOG_COFIG
{
static uint64_t g_iLogFileMaxSize = 2L * 1024 * 1024 * 1024;
static uint64_t g_iCheckLogInterval = 20;//20s
inline int checkdir(const char* sPathName)
{
char DirName[256];
strcpy(DirName, sPathName);
int i, len = strlen(DirName);
if (DirName[len - 1] != '/')
strcat(DirName, "/");
len = strlen(DirName);
for (i = 1; i < len; i++)
{
if (DirName[i] == '/')
{
DirName[i] = 0;
if (access(DirName, 0) != 0)
{
if (mkdir(DirName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1)
{
perror(" mkdir error");
return -1;
}
}
DirName[i] = '/';
}
}
return 0;
}
/*
** log class, it is not thread safe.
*/
void* WriteLogThread(void* param);
struct InfoItem
{
struct tm t;
bool bLogTime;
char strInfo[INFO_BUF_LEN];
int GetLen() { return (uint64_t)(&strInfo[0]) - (uint64_t)(this) + strlen(strInfo) + 1; }
};
class CLogFile
{
private:
char* log_filename;
char* log_dirpath;
char* log_fullpath_filename;
int m_bRecordByDay;
int current_day;
FILE* file_handle;
DataQueue m_InfoQueue; //
bool m_bExit;
bool m_bUseQueue;
bool m_bWriteFinish;
pthread_t m_pWriteThreadHandle;
private:
void set_log_fullpath_filename(struct tm* t)
{
if (log_fullpath_filename == NULL)
log_fullpath_filename = (char*)calloc(strlen(log_dirpath) + 1 + 8 + 1 + strlen(log_filename) + 1 + 10, sizeof(char));
if (m_bRecordByDay)
{
current_day = t->tm_mday;
sprintf(log_fullpath_filename, "%s%02d%02d%02d/", log_dirpath, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
}
else
sprintf(log_fullpath_filename, "%s", log_dirpath);
checkdir(log_fullpath_filename);
strcat(log_fullpath_filename, log_filename);
}
void open_log_file()
{
file_handle = fopen(log_fullpath_filename, "a");
if (file_handle == NULL)
{//2019.07.18------------------
fprintf(stderr, "\n----------------------------------------------------------------\n");
fprintf(stderr, "***Fatal error: can't open file: %s", log_fullpath_filename);
fprintf(stderr, "----------------------------------------------------------------\n");
fflush(stderr);
exit(1);
}
}
struct tm* get_time()
{
time_t clock;
time(&clock);
struct tm* t = localtime(&clock);
return t;
}
void print_time()
{
struct tm* t = get_time();
fprintf(file_handle, "%4d-%02d-%02d %02d:%02d:%02d\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
}
public:
FILE* GetFileHande() { return file_handle; }
CLogFile(const char* dir, const char* name, int byDay, bool bUseQueue = false)
{
m_bRecordByDay = byDay;
m_bUseQueue = bUseQueue;
file_handle = NULL;
m_bWriteFinish = true;
m_bExit = false;
log_dirpath = (char*)calloc(strlen(dir) + 10, sizeof(char));
log_filename = (char*)calloc(strlen(name) + 10, sizeof(char));
strcpy(log_dirpath, dir);
int i, len = strlen(log_dirpath);
if (log_dirpath[len - 1] != '/')
strcat(log_dirpath, "/");
strcpy(log_filename, name);
log_fullpath_filename = NULL;
struct tm* t = get_time();
set_log_fullpath_filename(t);
open_log_file();
if (bUseQueue)
{
m_InfoQueue.InitQueue(32 * 1024, 8 * 2048);
pthread_create(&m_pWriteThreadHandle, NULL, WriteLogThread, this);
}
}
~CLogFile()
{
m_bExit = true;
pthread_join(m_pWriteThreadHandle, NULL);
close_log_file();
free(log_filename);
free(log_dirpath);
free(log_fullpath_filename);
}
void append_one_item(int print_time_flag, const char* fmt, ...)
{//append the new info into the queue
char fmtbuf[INFO_BUF_LEN];
va_list arglist;
if (m_bExit) return;
InfoItem tmpInfoItem;
va_start(arglist, fmt);
vsprintf(fmtbuf, fmt, arglist);
va_end(arglist);
snprintf(tmpInfoItem.strInfo, sizeof(tmpInfoItem.strInfo), fmtbuf);
tmpInfoItem.t = *get_time();
tmpInfoItem.bLogTime = print_time_flag;
if (m_bUseQueue)
m_InfoQueue.EnQueue((void*)(&tmpInfoItem), tmpInfoItem.GetLen());
else
{
writeItem2File(&tmpInfoItem);
}
}
void checksize(uint64_t iSize)
{
uint64_t iFileBytes = ftell(file_handle);
if (iFileBytes > iSize)
{
char strBackupName[500];
char strCurTime[20];
time_t tt;
tt = time(NULL);
strftime(strCurTime, sizeof(strCurTime), "%Y.%m.%d-%H.%M.%S", localtime(&tt));//产生“YYYY-MM-DD hh:mm:ss”格式的字符串。
sprintf(strBackupName, "%s-bak%s", log_fullpath_filename, strCurTime);
close_log_file();
rename(log_fullpath_filename, strBackupName);//把原文件重命名为新的文件名
open_log_file();
}
};
void writeItem2File(InfoItem* pInfoItem)
{
if (file_handle == NULL)
{//2019.07.18
return;
}
int iBytes;
if ((m_bRecordByDay) && (pInfoItem->t.tm_mday != current_day))
{//change_to_new_day
close_log_file();
current_day = pInfoItem->t.tm_mday;
set_log_fullpath_filename(&(pInfoItem->t));
open_log_file();
char strCmd[300];
sprintf(strCmd, "find %s -type d -mtime +30 -exec rm -rf {} \\;", log_dirpath);
system(strCmd);
}
if (pInfoItem->bLogTime)
{
iBytes = fprintf(file_handle, "%s %4d.%02d.%02d %02d:%02d:%02d\n",
pInfoItem->strInfo,
pInfoItem->t.tm_year + 1900, pInfoItem->t.tm_mon + 1, pInfoItem->t.tm_mday,
pInfoItem->t.tm_hour, pInfoItem->t.tm_min, pInfoItem->t.tm_sec);
}
else
iBytes = fprintf(file_handle, "%s", pInfoItem->strInfo);
if (iBytes < 0)
fprintf(stderr, "***writing \"%s\" to log failed! errno:%d, %s\n", pInfoItem->strInfo, errno, strerror(errno));
else
fflush(file_handle);
}
void writeQueue2File()
{
unsigned int iWiteItems = 0;
InfoItem* pInfoItem;
struct tm* t;
m_bWriteFinish = false;
unsigned n = 0;
int iBytes;
while (!m_bExit)
{
iBytes = m_InfoQueue.DeQueue((void**)(&pInfoItem));
if (iBytes <= 0)
{
sleep(1);//10ms
continue;
}
writeItem2File(pInfoItem);
if ((++n % g_iCheckLogInterval == 0) && (!m_bRecordByDay))
checksize(g_iLogFileMaxSize);// 2L * 1024 * 1024 * 1024);//不超过2GB
}
t = get_time();
fprintf(file_handle, "...finish and exit! @%4d.%02d.%02d %02d:%02d:%02d\n\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
fflush(file_handle);
m_bWriteFinish = true;
}
void close_log_file()
{
if (file_handle != NULL)
{
fflush(file_handle);
fclose(file_handle);
file_handle = NULL;
}
}
void clear_expired_logs()
{
}
};
//void * WriteLogThread(void* param)
//{
// CLogFile *pLog = (CLogFile*)(param);
// pLog->writeQueue2File();//
//}
//---------------------------------------------------------------------------------------------------
}
#endif

17
logqueueconfig/mak Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
if echo "$@" | grep -iq r; then
Param1="RELEASE_VERSION=yes"
else
Param1="DEBUG_VERSION=yes"
fi
make clean
clear
if echo "$@" | grep -iq e; then
#------ compile for euler -------------------
make -j 4 $Param1 CXX=arm-ztkp_openeuler-linux-gnueabi-g++ AR=arm-ztkp_openeuler-linux-gnueabi-ar OUTPUT_LIB_DIR=../../libs-euler/
else
#------- compile for arm32 ------------------
make -j 4 $Param1 CXX=arm-linux-gnueabihf-g++ AR=arm-linux-gnueabihf-ar OUTPUT_LIB_DIR=../../libs-arm32/
fi

23
mak Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
mkdir -p build && cd $(dirname $0)/build
if echo "$@" | grep -iq r; then
BUILD_TYPE=RELEASE
else
BUILD_TYPE=DEBUG
fi
if echo "$@" | grep -iq e; then
#------ compile for euler -------------------
TOOLCHAIN=../../toolchains/armv7l_openeuler_setup.cmake
BUILD_PREFIX=armv7l-openeular
else
#------- compile for arm32 ------------------
TOOLCHAIN=../../toolchains/armv7l_linux_setup.cmake
BUILD_PREFIX=armv7l-linux
fi
mkdir -p ${BUILD_PREFIX} && cd ${BUILD_PREFIX}
rm -f CMakeCache.txt
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} -DEXECUTABLE_OUTPUT_PATH=../../bin/${BUILD_PREFIX} ../..
cmake --build . -j 4 --

415
scripts/unittest.py Normal file
View File

@ -0,0 +1,415 @@
#!/usr/bin/python3
import os
import sys
from io import StringIO
from argparse import ArgumentParser
from enum import IntEnum
from glob import glob
from subprocess import run
from time import time
from traceback import print_exception
from typing import List, NoReturn, Optional
__version__ = "0.1.2"
if sys.stdout.isatty():
TTY_COLOR_RED = "\033[31m"
TTY_COLOR_RED_BOLD = "\033[1;31m"
TTY_COLOR_GREEN = "\033[32m"
TTY_COLOR_GREEN_BOLD = "\033[1;32m"
TTY_COLOR_YELLOW = "\033[33m"
TTY_COLOR_YELLOW_BOLD = "\033[1;33m"
TTY_COLOR_CYAN_BOLD = "\033[1;36m"
TTY_COLOR_CLEAR = "\033[0m"
TTY_COLUMNS_SIZE = os.get_terminal_size().columns
else:
TTY_COLOR_RED = ""
TTY_COLOR_RED_BOLD = ""
TTY_COLOR_GREEN = ""
TTY_COLOR_GREEN_BOLD = ""
TTY_COLOR_YELLOW = ""
TTY_COLOR_YELLOW_BOLD = ""
TTY_COLOR_CYAN_BOLD = ""
TTY_COLOR_CLEAR = ""
TTY_COLUMNS_SIZE = 80
def print_separator_ex(lc: str, title: str, color: str) -> None:
len_str = len(title)
print(f"{TTY_COLOR_CLEAR}{color}", end='') # 设置颜色样式
if len_str + 2 > TTY_COLUMNS_SIZE:
print(title)
else:
print(f" {title} ".center(TTY_COLUMNS_SIZE, lc))
print(TTY_COLOR_CLEAR, end='') # 重置颜色为默认
class CTestCaseStatus(IntEnum):
NOT_RUN = -1
PASSED = 0
ERROR = 1
FAILED = 16
SKIPPED = 32
SETUP_FAILED = 17
TEARDOWN_FAILED = 18
class CTestCaseCounter:
__slots__ = ["total_count", "passed", "error", "failed", "skipped"]
def __init__(self) -> None:
self.total_count = 0
self.passed: 'set[CTestCase]' = set()
self.error: 'set[CTestCase]' = set()
self.failed: 'set[CTestCase]' = set()
self.skipped: 'set[CTestCase]' = set()
def update(self, test_case: "CTestCase") -> None:
self.total_count += 1
if test_case.status == CTestCaseStatus.PASSED:
self.passed.add(test_case)
elif test_case.status == CTestCaseStatus.ERROR:
self.error.add(test_case)
elif test_case.status == CTestCaseStatus.SKIPPED:
self.skipped.add(test_case)
elif test_case.status in [CTestCaseStatus.FAILED, CTestCaseStatus.SETUP_FAILED, CTestCaseStatus.TEARDOWN_FAILED]:
self.failed.add(test_case)
else:
raise ValueError(f"{test_case.status} is not a valid status for counter")
def clone(self) -> "CTestCaseCounter":
counter = CTestCaseCounter()
counter.total_count = self.total_count
counter.passed = self.passed
counter.error = self.error
counter.failed = self.failed
counter.skipped = self.skipped
return counter
def __add__(self, other: "CTestCaseCounter") -> "CTestCaseCounter":
counter = self.clone()
counter += other
return counter
def __iadd__(self, other: "CTestCaseCounter") -> "CTestCaseCounter":
self.total_count += other.total_count
self.passed.update(other.passed)
self.error.update(other.error)
self.failed.update(other.failed)
self.skipped.update(other.skipped)
return self
@property
def status(self) -> CTestCaseStatus:
if self.error:
return CTestCaseStatus.ERROR
elif self.failed:
return CTestCaseStatus.FAILED
elif self.skipped:
return CTestCaseStatus.SKIPPED
elif self.passed:
return CTestCaseStatus.PASSED
else:
return CTestCaseStatus.NOT_RUN
def __repr__(self) -> str:
return f"<counter total: {self.total_count}, passed: {len(self.passed)}, error: {len(self.error)}, failed: {len(self.failed)}, skipped: {len(self.skipped)}>"
def __str__(self) -> str:
ss = StringIO()
ss.write(f"total: {self.total_count}")
if self.passed:
ss.write(f", passed: {len(self.passed)}")
elif self.skipped:
ss.write(f", skipped: {len(self.skipped)}")
elif self.failed:
ss.write(f", failed: {len(self.failed)}")
elif self.error:
ss.write(f", error: {len(self.error)}")
return ss.getvalue()
class CTestCase:
__slots__ = ["id", "name", "status", "file", "result", "error_info"]
def __init__(self, id: int, name: str, file: "CTestCaseFile") -> None:
self.id = id
self.name = name
self.file = file
self.status = CTestCaseStatus.NOT_RUN
self.result = None
self.error_info = None
def run(self, *, verbose: bool = False, capture: bool = True) -> CTestCaseStatus:
try:
sys.stdout.flush()
sys.stderr.flush()
self.result = run([self.file.path, "--unittest", str(self.id)], capture_output=capture)
except Exception:
self.status = CTestCaseStatus.ERROR
self.error_info = sys.exc_info()
if not capture:
print(TTY_COLOR_RED)
print_exception(*self.error_info)
print(TTY_COLOR_CLEAR)
except KeyboardInterrupt:
self.status = CTestCaseStatus.ERROR
self.error_info = sys.exc_info()
else:
code = self.result.returncode
if code in CTestCaseStatus.__members__.values():
self.status = CTestCaseStatus(code)
else:
self.status = CTestCaseStatus.ERROR
if verbose:
self.print_status_verbose()
else:
self.print_status()
return self.status
def print_status(self) -> None:
if self.status == CTestCaseStatus.PASSED:
print(f"{TTY_COLOR_GREEN}.{TTY_COLOR_CLEAR}", end='')
elif self.status in [CTestCaseStatus.FAILED, CTestCaseStatus.SETUP_FAILED, CTestCaseStatus.TEARDOWN_FAILED]:
print(f"{TTY_COLOR_RED}F{TTY_COLOR_CLEAR}", end='')
elif self.status == CTestCaseStatus.ERROR:
print(f"{TTY_COLOR_RED}E{TTY_COLOR_CLEAR}", end='')
elif self.status == CTestCaseStatus.SKIPPED:
print(f"{TTY_COLOR_YELLOW}s{TTY_COLOR_CLEAR}", end='')
else:
raise ValueError(f"invalid test case status: {self.status}")
def print_status_verbose(self) -> None:
if self.status == CTestCaseStatus.PASSED:
print(f"{self.name} {TTY_COLOR_GREEN}PASSED{TTY_COLOR_CLEAR}")
elif self.status == CTestCaseStatus.FAILED:
print(f"{self.name} {TTY_COLOR_RED}FAILED{TTY_COLOR_CLEAR}")
elif self.status == CTestCaseStatus.ERROR:
print(f"{self.name} {TTY_COLOR_RED}ERROR{TTY_COLOR_CLEAR}")
elif self.status == CTestCaseStatus.SETUP_FAILED:
print(f"{self.name} {TTY_COLOR_RED}SETUP FAILED{TTY_COLOR_CLEAR}")
elif self.status == CTestCaseStatus.TEARDOWN_FAILED:
print(f"{self.name} {TTY_COLOR_RED}TEARDOWN FAILED{TTY_COLOR_CLEAR}")
elif self.status == CTestCaseStatus.SKIPPED:
print(f"{self.name} {TTY_COLOR_YELLOW}SKIPPED{TTY_COLOR_CLEAR}")
else:
raise ValueError(f"invalid test case status: {self.status}")
def report(self) -> Optional[str]:
if self.status == CTestCaseStatus.PASSED:
return
elif self.status == CTestCaseStatus.SKIPPED:
return f"{TTY_COLOR_YELLOW}SKIPPED{TTY_COLOR_CLEAR} {self.name}"
elif self.status == CTestCaseStatus.ERROR:
if self.error_info:
print_separator_ex('_', self.name, TTY_COLOR_RED)
print(TTY_COLOR_RED, end='')
print_exception(*self.error_info)
print(TTY_COLOR_CLEAR, end='')
assert self.error_info[0]
if str(self.error_info[1]):
error = f"{self.error_info[0].__name__}: {self.error_info[1]}"
else:
error = f"{self.error_info[0].__name__}"
return f"{TTY_COLOR_RED}ERROR{TTY_COLOR_CLEAR} {self.name} - {error}"
else:
assert self.result
if self.result.stderr:
print_separator_ex('_', self.name, TTY_COLOR_RED)
print(TTY_COLOR_RED, end='')
print(self.result.stderr.decode("utf-8"), end='')
print(TTY_COLOR_CLEAR, end='')
if self.result.stdout:
print_separator_ex('-', "Captured stdout", '')
print(self.result.stdout.decode("utf-8"), end='')
return f"{TTY_COLOR_RED}ERROR{TTY_COLOR_CLEAR} {self.name} - RuntimeError ({self.result.returncode})"
elif self.status in [CTestCaseStatus.FAILED, CTestCaseStatus.SETUP_FAILED, CTestCaseStatus.TEARDOWN_FAILED]:
assert self.result
if self.result.stderr:
print_separator_ex('_', self.name, TTY_COLOR_RED)
print(TTY_COLOR_RED, end='')
print(self.result.stderr.decode("utf-8"), end='')
print(TTY_COLOR_CLEAR, end='')
if self.result.stdout:
if self.result.stderr:
print_separator_ex('-', "Captured stdout", '')
else:
print_separator_ex('_', self.name, '')
print(self.result.stdout.decode("utf-8", "replace"), end='')
if self.status == CTestCaseStatus.FAILED:
return f"{TTY_COLOR_RED}FAILED{TTY_COLOR_CLEAR} {self.name}"
elif self.status == CTestCaseStatus.SETUP_FAILED:
return f"{TTY_COLOR_RED}FAILED{TTY_COLOR_CLEAR} {self.name} - SetupError"
else:
return f"{TTY_COLOR_RED}FAILED{TTY_COLOR_CLEAR} {self.name} - TeardownError"
else:
raise ValueError(f"invalid test case status: {self.status}")
class CTestCaseFile:
__slots__ = ["path", "test_cases", "collect_result", "collect_error_info", "counter"]
def __init__(self, path: str) -> None:
self.path = path
self.test_cases: List[CTestCase] = []
self.collect_result = None
self.collect_error_info = None
def collect(self) -> int:
try:
result = run([self.path, "--collect"], capture_output=True)
except Exception:
self.collect_error_info = sys.exc_info()
return 0
if result.returncode != 0:
self.collect_result = result
return 0
for id, name in enumerate(result.stdout.decode("ascii").split()):
self.test_cases.append(CTestCase(id, name, self))
return len(self.test_cases)
def run(self, verbose: bool = False, capture: bool = True) -> CTestCaseCounter:
counter = CTestCaseCounter()
if not verbose:
print(self.path, end=' ')
for i in self.test_cases:
if verbose:
print(self.path, end='::')
i.run(verbose=verbose, capture=capture)
counter.update(i)
if not verbose:
print()
return counter
def report(self) -> List[str]:
if self.collect_result is not None:
print_separator_ex('_', f"ERROR collecting {self.path}", TTY_COLOR_RED_BOLD)
print(TTY_COLOR_RED, end='')
print(self.collect_result.stderr.decode())
print(TTY_COLOR_CLEAR, end='')
if self.collect_result.stdout:
print_separator_ex('-', "Captured stdout", '')
print(self.collect_result.stdout.decode())
return [f"ERROR {self.path} - CollectError ({self.collect_result.returncode})"]
elif self.collect_error_info is not None:
assert self.collect_error_info[0]
print_separator_ex('_', f"ERROR collecting {self.path}", TTY_COLOR_RED_BOLD)
print(TTY_COLOR_RED, end='')
print_exception(*self.collect_error_info)
print(TTY_COLOR_CLEAR, end='')
return [f"ERROR{TTY_COLOR_CLEAR} {self.path} - {self.collect_error_info[0].__name__}: {self.collect_error_info[1]}"]
return list(filter(None, (i.report() for i in self.test_cases)))
@property
def error(self) -> bool:
return self.collect_result is not None or self.collect_error_info is not None
def report_collect_error(start_time: float, *error_files: CTestCaseFile) -> NoReturn:
print_separator_ex('=', "ERRORS", '')
summary = []
for i in error_files:
summary.extend(i.report())
print_separator_ex('=', "short test summary info", TTY_COLOR_CYAN_BOLD)
for i in summary:
print(i)
print_separator_ex('!', f"Interrupted: {len(error_files)} error during collection", '')
cur_time = time()
print_separator_ex('=', f"{len(summary)} error in {cur_time - start_time:.2f}s", TTY_COLOR_RED_BOLD)
sys.exit(1)
def report_no_ran(start_time: float) -> NoReturn:
cur_time = time()
print_separator_ex('=', f"no tests ran in {cur_time - start_time:.2f}s", TTY_COLOR_YELLOW)
sys.exit()
def report(start_time: float, counter: CTestCaseCounter, *, show_capture: bool = True) -> NoReturn:
cur_time = time()
summary = []
if counter.error:
if show_capture:
print_separator_ex('=', "ERRORS", '')
for i in counter.error:
summary.append(i.report())
if counter.failed:
if show_capture:
print_separator_ex('=', "FAILURES", TTY_COLOR_RED)
for i in counter.failed:
summary.append(i.report())
if counter.skipped:
for i in counter.skipped:
summary.append(i.report())
if summary:
print_separator_ex('=', "short test summary info", TTY_COLOR_CYAN_BOLD)
for i in summary:
print(i)
if counter.status in [CTestCaseStatus.FAILED, CTestCaseStatus.ERROR]:
color = TTY_COLOR_RED_BOLD
elif counter.status == CTestCaseStatus.SKIPPED:
color = TTY_COLOR_YELLOW_BOLD
else:
color = TTY_COLOR_GREEN_BOLD
print_separator_ex('=', f"{counter} in {cur_time - start_time:.2f}s", color)
if counter.status in [CTestCaseStatus.FAILED, CTestCaseStatus.ERROR]:
sys.exit(1)
sys.exit()
if __name__ == "__main__":
parser = ArgumentParser("unittest", description="Run unit tests")
parser.add_argument("path", nargs='+', help="path to the test directory or file", default="./test_*")
parser.add_argument("-V", "--version", action="version", version=__version__)
parser.add_argument("-v", "--verbose", action="store_true", help="verbose output")
parser.add_argument("-s", "--no-capture", action="store_false", help="capture stdout and stderr")
namespace = parser.parse_args()
print_separator_ex("=", "test session starts", "")
print(f"platform: {sys.platform} -- Python {sys.version.split(' ')[0]}, c_unittest {__version__}")
print(f"rootdir: {os.getcwd()}")
files: List[CTestCaseFile] = []
total = 0
error_files = []
start_time = time()
paths = []
for p in namespace.path:
if '*' in p:
paths.extend(glob(p, recursive=True))
else:
paths.append(p)
for p in paths:
f = CTestCaseFile(p)
total += f.collect()
if f.error:
error_files.append(f)
else:
files.append(f)
if error_files:
print(f"collected {total} items / {len(error_files)} error\n")
report_collect_error(start_time, *error_files)
else:
print(f"collected {total} items\n")
if total == 0:
report_no_ran(start_time)
counter = CTestCaseCounter()
for f in files:
counter += f.run(verbose=namespace.verbose, capture=namespace.no_capture)
report(start_time, counter, show_capture=namespace.no_capture)

16
scripts/unittest.sh Normal file
View File

@ -0,0 +1,16 @@
width=$(stty size | awk '{print $2}')
# run add test_* in bin/*
for i in `ls bin/test_*`
do
echo
printf '%*s\n' "$width" '' | tr ' ' '*'
echo run test file: $i
$i $@
if [ $? -ne 0 ]; then
echo "\033[0m\033[1;31mtest $i failed\033[0m"
fi
echo
done
# printf '%*s\n' "$width" '' | tr ' ' '*'

327
src/comframe.cpp Executable file
View File

@ -0,0 +1,327 @@
// #define COM_FRAME_DEBUG
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "comframe.h"
#include "telemetry.h"
//int get_com_tty_id(const char* path, unsigned int baudRate) {
// int tty_id = open((const char*)path, O_RDWR | O_NOCTTY);
// if (tty_id < 0)
// {
// PrintFilePos(); fprintf(stderr, "...error open tty failed: %s @%s:%d\n", strerror(errno), __FILE__, __LINE__);
// exit(__LINE__);
// }
// init_tty(tty_id, baudRate);
// PrintFilePos(); printf("COM '%s' is ready!\n", path);
// return tty_id;
//}
//
//void init_tty(int tty_id, unsigned int baudRate) {
// struct termios options, old;
// // deploy usart par
// memset(&options, 0, sizeof(options));
// int ret = tcgetattr(tty_id, &old);
// if (ret != 0)
// {
// PrintFilePos(); fprintf(stderr, "...error tcgetattr() failed: %s\n @%s:%d", strerror(errno), __FILE__, __LINE__);
// exit(__LINE__);
// }
//
//
// tcflush(tty_id, TCIOFLUSH);
//
// switch (baudRate) {
// case 9600:
// cfsetispeed(&options, B9600);
// cfsetospeed(&options, B9600);
// break;
// case 19200:
// cfsetispeed(&options, B19200);
// cfsetospeed(&options, B19200);
// break;
// case 38400:
// cfsetispeed(&options, B38400);
// cfsetospeed(&options, B38400);
// break;
// case 57600:
// cfsetispeed(&options, B57600);
// cfsetospeed(&options, B57600);
// break;
// case 115200:
// cfsetispeed(&options, B115200);
// cfsetospeed(&options, B115200);
// break;
// case 576000:
// cfsetispeed(&options, B576000);
// cfsetospeed(&options, B576000);
// break;
// case 921600:
// cfsetispeed(&options, B921600);
// cfsetospeed(&options, B921600);
// break;
// case 2000000:
// cfsetispeed(&options, B2000000);
// cfsetospeed(&options, B2000000);
// break;
// case 3000000:
// cfsetispeed(&options, B3000000);
// cfsetospeed(&options, B3000000);
// break;
// default:
// PrintFilePos(); printf("****Error: baud rate %u too low!\n", baudRate);
// exit(__LINE__);
// break;
// }
// switch (1)
// {
// case 0:
// options.c_cflag &= ~PARENB;
// options.c_cflag &= ~INPCK;
// break;
// case 1:
// options.c_cflag |= (PARODD //使用奇校验代替偶校验
// | PARENB);//校验位有效
// options.c_iflag |= INPCK; //校验有效
// break;
// case 2:
// options.c_cflag |= PARENB;
// options.c_cflag &= ~PARODD;
// options.c_iflag |= INPCK;
// break;
// case 3:
// options.c_cflag &= ~PARENB;
// options.c_cflag &= ~CSTOPB;
// break;
// default:
// options.c_cflag &= ~PARENB;
// break;
// }
//
// options.c_cflag |= (CLOCAL | CREAD);
// options.c_cflag &= ~CSIZE;
// options.c_cflag &= ~CRTSCTS;
// options.c_cflag |= CS8;
// options.c_cflag &= ~CSTOPB;
// options.c_oflag = 0;
// options.c_lflag = 0;
// options.c_cc[VTIME] = 0;
// options.c_cc[VMIN] = 0;
// // 启用输出的XON/XOFF控制字符
// // Enable software flow control (XON/XOFF) for both input and output
// options.c_iflag |= (IXON | IXOFF); // Enable input and output XON/XOFF control characters
// options.c_oflag |= (IXON | IXOFF); // Enable input and output XON/XOFF control characters
// tcflush(tty_id, TCIFLUSH);
//
// if ((tcsetattr(tty_id, TCSANOW, &options)) != 0)
// {
// PrintFilePos(); fprintf(stderr, "...error tcsetattr failed:%s\n", strerror(errno));
// exit(__LINE__);
// }
//}
uint16_t ComFramePayload_EvalChecksum(const struct ComFrameHeader* header, const void* payload) {
size_t length = ComFrame_PAYLOAD_LENGTH(header);
const uint16_t* buf = (const uint16_t*)payload;
uint16_t sum = 0;
while (length > 1)
{
sum ^= *(buf++);
length -= 2;
}
if (length == 1) {
sum ^= (COM_FRAME_PADDING_DATA << 8) + *((uint8_t*)buf);
}
return sum;
}
bool ComFramePayload_VerifyChecksum(const struct ComFrameHeader* header, const void* payload, uint16_t checksum) {
return ComFramePayload_EvalChecksum(header, (uint8_t*)payload) != checksum;
}
ComFrame* ComFrame_FromHeaderEx(const struct ComFrameHeader* header, const void* payload, uint16_t checksum, const bool swap_endian) {
uint16_t length = ComFrame_PAYLOAD_LENGTH(header);
ComFrame* frame = ComFrame_NewUninited(length);
if (frame == NULL)
return NULL;
memcpy(&frame->header, header, sizeof(struct ComFrameHeader));
if (swap_endian) {
ComFrameHeader_SwapEndian(ComFrame_HEADER(frame));
}
ComFrame_UpdatePayload(frame, payload, length, swap_endian);
return frame;
}
ComFrame* ComFrame_FromHeader(const struct ComFrameHeader* header, const void* payload, const bool swap_endian) {
uint16_t checksum = ComFramePayload_EvalChecksum(header, payload);
ComFrame* frame = ComFrame_FromHeaderEx(header, payload, checksum, swap_endian);
return frame;
}
ComFrame* ComFrame_New(uint16_t address, uint16_t type, const void* payload, uint16_t payload_length, const bool swap_endian) {
ComFrame* frame = ComFrame_NewUninited(payload_length);
ComFrameHeader_Init(ComFrame_HEADER(frame), address, type, 0);
if (swap_endian) {
ComFrameHeader_SwapEndian(ComFrame_HEADER(frame));
}
ComFrame_UpdatePayload(frame, payload, payload_length, swap_endian);
return frame;
}
void ComFrame_UpdatePayload(ComFrame* frame, const void* payload, const uint16_t payload_length, const bool swap_endian) {
if (payload)
memcpy(&frame->payload, payload, payload_length);
if (payload_length & 1) {
frame->payload[payload_length] = COM_FRAME_PADDING_DATA;
}
ComFrame_HEADER(frame)->length = payload_length + 1;
uint16_t checksum = ComFramePayload_EvalChecksum(ComFrame_HEADER(frame), &frame->payload);
memcpy((uint8_t*)(&frame->payload) + ComFrame_PAYLOAD_ALIGNED_LENGTH(frame), &checksum, sizeof(uint16_t));
if (swap_endian) {
ComFrame_HEADER(frame)->length = byteswaps(ComFrame_HEADER(frame)->length);
}
}
bool ComFrame_Verify(const ComFrame* frame, const bool swap_endian) {
struct ComFrameHeader header;
if (swap_endian) {
memcpy(&header, frame, sizeof(ComFrameHeader));
ComFrameHeader_SwapEndian(&header);
}
uint16_t checksum = ComFrame_Checksum(frame, swap_endian);
return ComFramePayload_VerifyChecksum((swap_endian) ? &header : ComFrame_HEADER(frame), frame->payload, checksum);
}
ssize_t ComFrame_Send(int tty_id, const ComFrame* frame, const bool swap_endian) {
uint32_t length = ComFrame_Length(frame, swap_endian);
char* buf = (char*)frame;
#ifdef COM_FRAME_DEBUG
PrintFilePos(); printf("send length: %d\n", ComFrame_Length(frame, swap_endian));
PrintFilePos(); printf("send checksum: %04x\n", ComFrame_Checksum(frame, swap_endian));
#endif
while (length > 0) {
ssize_t written = write(tty_id, buf, length);
if (written < 0) return written;
length -= written;
buf += written;
}
return 0;
}
ssize_t SendTelemetryErrorMsg(int tty_id, uint16_t address, uint16_t code, const bool swap_endian) {
ComFrame* f_error = NewTelemetryErrorMsg(address, code, true);
ssize_t wr = ComFrame_Send(tty_id, f_error, swap_endian);
ComFrame_Del(f_error);
return wr;
}
#define COM_FRAME_RECEIVE_PRED_SIZE (2 * sizeof(ComFrameHeader))
ComFrame* ComFrame_ReceiveEx(int tty_id, void* buffer, const size_t buffer_size, size_t* p_offset, size_t* p_cached_size, bool* p_run_flag, const bool swap_endian) {
bool find_head = false;
ComFrame* frame = NULL;
ssize_t recv_size;
size_t pred_size = COM_FRAME_RECEIVE_PRED_SIZE; // min buffer size to keep,
size_t offset = (p_offset) ? *p_offset : 0; // scanned data size
size_t cached_size = (p_cached_size) ? *p_cached_size : 0; // read data size
assert(buffer_size >= pred_size);
if (buffer == NULL) {
buffer = (uint8_t*)malloc(buffer_size);
}
bool _keep_run = true;
if (p_run_flag == NULL) {
p_run_flag = &_keep_run;
}
// clear the cache when the remaining length of the cache is less than 2 times the frame length or find the header
if (offset && buffer_size - cached_size < pred_size) {
cached_size -= offset;
memmove(buffer, ((uint8_t*)buffer)+offset, cached_size);
offset = 0;
}
while (*p_run_flag) {
recv_size = read(tty_id, ((uint8_t*)buffer) + cached_size, buffer_size - cached_size);
if (recv_size <= 0 && cached_size == offset) {
usleep(2);
continue;
}
#ifdef COM_FRAME_DEBUG
PrintFilePos(); printf(
"receive com data (received=%zd,cached=%zu,number=%04x)\n",
recv_size, cached_size, ComFrame_HEADER(buffer)->magic_number
);
PrintFilePos(); printf("buffer: ");
for (size_t i = 0; i < cached_size + recv_size; ++i) {
PrintFilePos(); printf("%02x", ((uint8_t*)buffer)[i]);
}
putchar('\n');
#endif
cached_size += recv_size;
if (!find_head) {
// update offset to scan the header
for (; offset + sizeof(ComFrameHeader) < cached_size; ++offset) {
find_head = !ComFrameHeader_Check(ComFrame_HEADER(((uint8_t*)buffer) + offset), swap_endian);
if (find_head) {
if (swap_endian)
ComFrameHeader_SwapEndian(ComFrame_HEADER(((uint8_t*)buffer) + offset));
#ifdef COM_FRAME_DEBUG
PrintFilePos(); printf("find valid data (length=%u)\n", ComFrame_LENGTH(((uint8_t*)buffer)+offset));
#endif
pred_size = ComFrame_LENGTH(((uint8_t*)buffer) + offset);
if (pred_size > buffer_size) {
PrintFilePos(); fprintf(stderr, "data size is too large (%zu)\n", pred_size);
find_head = false;
pred_size = COM_FRAME_RECEIVE_PRED_SIZE;
continue;
}
break;
}
}
// clear the cache when the remaining length of the cache is less than 2 times the frame length or find the header
if (offset && buffer_size - cached_size < pred_size) {
cached_size -= offset;
memmove(buffer, ((uint8_t*)buffer)+offset, cached_size);
offset = 0;
}
}
if (find_head && cached_size >= pred_size + offset) {
// all data received
frame = (ComFrame*)((uint8_t*)buffer + offset);
offset += pred_size; // update offset for next run
goto end;
}
}
end:
if (p_offset)
*p_offset = offset;
if (p_cached_size)
*p_cached_size = cached_size;
return frame;
}
ComFrame* ComFrame_Receive(int tty_id, const size_t buffer_size, const bool swap_endian) {
size_t offset = 0;
auto raw_frame = ComFrame_ReceiveEx(tty_id, NULL, buffer_size, &offset, NULL, NULL, swap_endian);
// correct the offset
offset -= ComFrame_LENGTH(raw_frame);
auto frame = (ComFrame*)(((char*)raw_frame) - offset);
// move the data to the buffer head, so that the data can be free safely
if (offset) {
memmove(frame, raw_frame, ComFrame_LENGTH(raw_frame));
}
return frame;
}

44
src/event.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <unordered_map>
#include <string>
#include <vector>
#include <tuple>
#include "event.h"
#include "dataqueue.hpp"
std::unordered_map<std::string, std::vector<std::pair<event_callback, void*>>> _events;
bool _event_thread_running = false;
DataQueue<std::tuple<const char*, event_callback, void*, void*>> _event_queue;
void sys_event(const char *event_name, void *args) {
auto it = _events.find(event_name);
if (it != _events.end()) {
for (auto &pair : it->second) {
pair.first(event_name, args, pair.second);
}
}
}
int sys_event_register(const char *event_name, event_callback callback, void* user_data) {
_events[event_name].push_back(std::make_pair(callback, user_data));
}
int sys_event_unregister(const char *event_name, event_callback callback) {
auto it = _events.find(event_name);
if (it != _events.end()) {
for (auto it2 = it->second.begin(); it2 != it->second.end(); it2++) {
if (it2->first == callback) {
it->second.erase(it2);
return 0;
}
}
}
}
void* sys_event_thread(void*) {
_event_thread_running = true;
while (_event_thread_running) {
auto [event_name, callback, args, user_data] = _event_queue.Pop();
callback(event_name, args, user_data);
}
return NULL;
}

3
src/extern_interface.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "extern_interface.h"

807
src/host_com.cpp Normal file
View File

@ -0,0 +1,807 @@
#include <errno.h>
#include <string.h>
#include <list>
#include <sys/sysinfo.h>
#include "telemetry.h"
#include "dataqueue.h"
#include "defines.h"
#include "command.h"
extern bool g_bKeepSysRuning;
void play_text(char* pTextStr, int iRepeat_times);
// int32_t g_iExternTimeDifference = 0;
static TelemetryCommandInfo g_receiveCommandInfo;
static std::list<TelemetryCommandInfo> g_sendCommandList;
static void* g_pRcvCmdQueue = NULL;
volatile static uint8_t g_iUpperHostTelemetryCount = 0;
volatile static uint8_t g_iTelemetryCount = 0;
// volatile static uint8_t telemetry_3package_loop_count[2] = {0, 0}; // added by gejp
// volatile static uint8_t telemetry_3package_count[2] = {0, 0}; // added by gejp
volatile bool need_resend = false;
volatile bool g_bNeedReboot = false;
volatile int32_t g_iMS2Reboot = 1000;
/* create a msg data for upper host telemetry*/
void set_telemetry_host_data(TelemetryData4UpperHost* pTelemetryData) {
pTelemetryData->work_status = 0xAA;
pTelemetryData->com_status = 0xAA;
pTelemetryData->coprocessor1_status = 0xAA;
pTelemetryData->coprocessor2_status = 0xAA;
pTelemetryData->voice_circuit_status = 0xAA;
pTelemetryData->telemetry_count = g_iUpperHostTelemetryCount++;
if (g_bCloseASR) {
pTelemetryData->voice_mode = 0xAA;
}
else if (g_iSysState == SYS_STATUS_WAKE) {
pTelemetryData->voice_mode = 0x55;
}
else {
pTelemetryData->voice_mode = 0xA5;
}
if (g_iSysState == SYS_STATUS_WAKE) {
pTelemetryData->recognition_status = 0x55;
}
else {
pTelemetryData->recognition_status = 0xAA;
}
for (int i = 0; i < 8; i++) {
pTelemetryData->recognition_info[i] = 0;
}
pTelemetryData->particle_detection1 = 0xAA;
pTelemetryData->particle_detection2 = 0xAA;
pTelemetryData->particle_detection3 = 0xAA;
memcpy(&pTelemetryData->receive_command, &g_receiveCommandInfo, sizeof(TelemetryCommandInfo));
if (g_sendCommandList.empty()) {
memset(&pTelemetryData->send_command, 0x00, sizeof(TelemetryCommandInfo));
} else {
memcpy(&pTelemetryData->send_command, &g_sendCommandList.front(), sizeof(TelemetrySendCommandInfo));
}
//if (g_bVolumeKeyState) {
// pTelemetryData->volume_key_status = 0xAA;
//}
//else {
// pTelemetryData->volume_key_status = 0x55;
//}
//if (g_bWakeupKeyState) {
// pTelemetryData->wake_key_status = 0xAA;
//}
//else {
// pTelemetryData->wake_key_status = 0x55;
//}
//pTelemetryData->volume_key_status = g_bVolumeKeyState ? 0xAA : 0x55;
//pTelemetryData->wake_key_status = g_bWakeupKeyState ? 0xAA : 0x55;
pTelemetryData->volume_key_status = g_bVolumeKeyPressed ? 0xAA : 0x55;
pTelemetryData->wake_key_status = g_bWakeupKeyPressed ? 0xAA : 0x55;
g_bVolumeKeyPressed = false;
g_bWakeupKeyPressed = false;
pTelemetryData->key_status_backup = 0x55;
if ( g_iCurVolumeGrade == g_iVolumeGradeCnt-1 ) {//large
pTelemetryData->current_volume = 0x3;
}
else if ( g_iCurVolumeGrade == 0 ) {//small
pTelemetryData->current_volume = 0x1;
}
else {//midium
pTelemetryData->current_volume = 0x2;
}
pTelemetryData->system_version_high = g_iSysVerHigh;
pTelemetryData->system_version_low = g_iSysVerLow;
pTelemetryData->application_version_high = g_iAppVerHigh;
pTelemetryData->application_version_low = g_iAppVerLow;
// 2024.10.10
if (g_iEnableAlarmCode == 0) {
pTelemetryData->alarm_code[0] = 0xAA;
pTelemetryData->alarm_code[1] = 0xAA;
pTelemetryData->alarm_code[2] = 0xAA;
pTelemetryData->alarm_code[3] = 0xAA;
} else {
pTelemetryData->alarm_code[0] = g_iAlarmCode[0];
pTelemetryData->alarm_code[1] = g_iAlarmCode[1];
pTelemetryData->alarm_code[2] = g_iAlarmCode[2];
pTelemetryData->alarm_code[3] = g_iAlarmCode[3];
}
for (int i = 0; i < 5; i++) {
pTelemetryData->telemetry_backup[i] = 0xAA;
}
}
/* create a telemetry msg data for telemetry, by gejp 2024.10.03 */
void set_telemetry_data(TelemetryData* pTelemetryHostData, TelemetryCommandInfo *pReceiveCommandInfo) {
pTelemetryHostData->work_status = 0xAA;
pTelemetryHostData->com_status = 0xAA;
pTelemetryHostData->coprocessor1_status = 0xAA;
pTelemetryHostData->coprocessor2_status = 0xAA;
pTelemetryHostData->voice_circuit_status = 0xAA;
pTelemetryHostData->telemetry_count = g_iTelemetryCount++;
if (g_bCloseASR) {
pTelemetryHostData->voice_mode = 0xAA;
}
else if (g_iSysState == SYS_STATUS_WAKE) {
pTelemetryHostData->voice_mode = 0x55;
}
else {
pTelemetryHostData->voice_mode = 0xA5;
}
if (g_iSysState == SYS_STATUS_WAKE) {
pTelemetryHostData->recognition_status = 0x55;
}
else {
pTelemetryHostData->recognition_status = 0xAA;
}
pTelemetryHostData->particle_detection1 = 0xAA;
pTelemetryHostData->particle_detection2 = 0xAA;
pTelemetryHostData->particle_detection3 = 0xAA;
memcpy(&(pTelemetryHostData->receive_command), (void*)pReceiveCommandInfo, sizeof(TelemetryCommandInfo));
// above same as set_telemetry_host_data
// 5 cmd info in history
size_t i = 0;
for (auto& send_cmd : g_sendCommandList) {
pTelemetryHostData->send_command[i].seqid = send_cmd.seqid & 0xFF;
pTelemetryHostData->send_command[i].flag = 0x66;
pTelemetryHostData->send_command[i].time = send_cmd.time;
memcpy(pTelemetryHostData->send_command[i].code, send_cmd.code, 2);
i++;
}
for (; i < 5; i++) {
memset(pTelemetryHostData->send_command + i, 0x00, sizeof(TelemetrySendCommandInfo));
}
// the following same as set_telemetry_host_data
pTelemetryHostData->volume_key_status = g_bVolumeKeyPressed ? 0xAA : 0x55;
pTelemetryHostData->wake_key_status = g_bWakeupKeyPressed ? 0xAA : 0x55;
g_bVolumeKeyPressed = false;
g_bWakeupKeyPressed = false;
pTelemetryHostData->key_status_backup = 0x55;
if (g_iCurVolumeGrade == g_iVolumeGradeCnt-1) {//Large
pTelemetryHostData->current_volume = 0x3;
}
else if (g_iCurVolumeGrade == 0) {//Small
pTelemetryHostData->current_volume = 0x1;
}
else {//Medium
pTelemetryHostData->current_volume = 0x2;
}
pTelemetryHostData->system_version_high = g_iSysVerHigh;
pTelemetryHostData->system_version_low = g_iSysVerLow;
pTelemetryHostData->application_version_high = g_iAppVerHigh;
pTelemetryHostData->application_version_low = g_iAppVerLow;
}
__inline bool is_command(uint8_t* array, uint8_t* value, int size) {
for (int i = 0; i < size; i++) {
if (array[i] != value[i]) {
return false;
}
}
return true;
}
void* telemetry_host_com_thread(void* path) {//Integrated Business Unit, telemetry_com综合业务单元遥测口
int tty_id = g_iTelemetry_Com_tty_id; // TODO
const uint32_t buffer_size = 1024 * 100; // TODO
const uint16_t address = COM_FRAME_ADDRESS_VOIX; // TODO
size_t offset = 0, cached_size = 0;
uint8_t* buffer = new uint8_t[buffer_size];
uint8_t err_data[2];
uint64_t iTelemetryAnswerTimes = 0;
void* pDataBlock;
int iRet;
TelemetryCommandInfo TeleCmdInfo2Send;
TelemetryData TelemetryData2Send; // gejp 2024.10.03
memset(&TeleCmdInfo2Send, 0, sizeof(TelemetryCommandInfo));
while (g_bKeepSysRuning) {
// if ( (iTelemetryAnswerTimes % 3 == 0) && g_bNeedReboot) system("reboot");
ComFrame* frame = ComFrame_ReceiveEx(tty_id, buffer, buffer_size, &offset, &cached_size, &g_bKeepSysRuning, true);
//ComFrame* frame = ComFrame_ReceiveEx(tty_id, buffer, buffer_size, true);
if (ComFrame_Verify(frame, false)) {
if (SendTelemetryErrorMsg(tty_id, address, TELEMETRY_ERROR_CHECKSUM, true))
PrintFilePos(); fprintf(stderr, "send telemetry exception frame error: %s\n", strerror(errno));
continue;
}
if (ComFrame_HEADER(frame)->type != COM_FRAME_TYPE_TELEMETRY_REQUEST ||
ComFrame_PAYLOAD_LENGTH(frame) != 2 ) {
continue;
}
if (frame->payload[0] != 0xFF || frame->payload[1] != 0x11) {
if (SendTelemetryErrorMsg(tty_id, address, TELEMETRY_ERROR_TYPE, true))
PrintFilePos(); fprintf(stderr, "send telemetry exception frame error: %s\n", strerror(errno));
continue;
}
if(iTelemetryAnswerTimes % 3 == 0 && g_pRcvCmdQueue != NULL) {
iRet = DeDataQueue(g_pRcvCmdQueue, &pDataBlock);
if (iRet < sizeof(TelemetryCommandInfo))
{//empty
if (0)//2024.11.04 g_bNeedReboot)
{
system("reboot");
break;
}
}else
{
TeleCmdInfo2Send = *((TelemetryCommandInfo*)pDataBlock);
PrintFilePos(); printf("telemetry receive command (%d)\n", TeleCmdInfo2Send.seqid);
}
set_telemetry_data(&TelemetryData2Send, &TeleCmdInfo2Send);
}
//keep same every 3 answers, count from 0, 1, ...
TelemetryData2Send.send_count = iTelemetryAnswerTimes; //count from 0, 1, ..
iTelemetryAnswerTimes++;
ComFrame* pTeleAnswerMsg = ComFrame_New(address, COM_FRAME_TYPE_TELEMETRY_ANSWER, &TelemetryData2Send, sizeof(struct TelemetryData), true);
TelemetryData_SwapEndian((struct TelemetryData*)(pTeleAnswerMsg->payload));
if (ComFrame_Send(tty_id, pTeleAnswerMsg, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
// added by gejp start
// if (telemetry_3package_loop_count[0] == 0) {
// telemetry_3package_count[0] += 1;
// }
// telemetry_3package_loop_count[0] += 1;
// if (telemetry_3package_loop_count[0] == 3) {
// telemetry_3package_loop_count[0] = 0;
// }
// added by gejp end
ComFrame_Del(pTeleAnswerMsg);
//ComFrame_Del(frame);
//ComFrame_ReceiveFlushBuffer(ComFrame_LENGTH(frame), buffer, &cached_size);
}
delete [] buffer;
return NULL;
}
void SetCallMode(bool bCall)
{
if (bCall)
{
PrintFilePos(); printf("Open call with upper host\n");
g_bCallMode = true;
g_iCallRcvFrameCnt = 0;
g_iPlayedCallFrameCnt = 0;
g_iOrdinaryPlayStop = 1;
g_bCloseASR = true;
g_iInsertSlientSegCnt=0;
g_iPcmBlockBytes = g_iCallBytesPerBlockMS;
}
else
{
PrintFilePos(); printf("Close call with upper host\n");
g_bCallMode = false;
g_iPcmBlockBytes = 512 * g_iChannelCnt * 2;
}
}
//获取指令文本与报警码
struct AlarmNotice {
uint32_t iAlarmCode; //指令编码,小端序
const char* pAlarmText;
};
AlarmNotice g_AlarmTable[] = {
{0x61006100, "热控A回路压力报警"},
{0x62006200, "热控B回路压力报警"},
{0x63006300, "热控分系统,参数异常"},
{0x64006400, "环控水箱满提醒"},
{0x65006500, "氮瓶压力过低报警"},
{0x66006600, "氧瓶压力过低报警"},
{0x67006700, "返回舱,火灾报警"},
{0x68006800, "返回舱,总压告警"},
{0x69006900, "返回舱,氧分压告警"},
{0x70007000, "返回舱,二氧化碳,分压告警"},
{0x71007100, "内回路泵,壁,转速报警"},
{0x72007200, "循环副风机转速报警"},
{0x73007300, "着陆器密封舱,总压报警"},
{0x74007400, "着陆器密封舱,氧分压报警"},
{0x75007500, "着陆器密封舱,二氧化碳分压报警"},
{0x76007600, "着陆器密封舱,火灾报警"},
{0x77007700, "着陆器辐射器,泄漏报警"},
{0x01000100, "起飞"},
{0x02000200, "船箭分离"},
{0x03000300, "组合体停靠"},
{0x04000400, "组合体分离"},
{0x05000500, "服返分离"},
{0x06000600, "回收开始"},
{0x07000700, "着陆"},
{0x08000800, "逃逸塔逃逸"},
{0x09000900, "预留"},
{0x0A000A00, "逃逸塔分离"},
{0x11001100, "整船逃逸"},
{0x71177117, "五分钟准备"},
{0x72177217, "一分钟准备"},
{0x73177317, "对接准备"},
{0x74177417, "对接环接触"},
{0x75177517, "分离完成"},
{0x76177617, "整流罩分离"},
{0x77177717, "帆板展开"},
{0x78177817, "船箭分离准备"},
{0x79177917, "帆板展开准备"},
{0x7A177A17, NULL}, // 预留
{0x7B177B17, "三十分钟后变轨"},
{0x7C177C17, "十分钟后变轨"},
{0x7D177D17, "五分钟后变轨"},
{0x7E177E17, "轨控发动机,开机指令发出"},
{0x7F177F17, "轨控发动机,关机指令发出"},
{0x80178017, "变轨结束"},
{0x81178117, "GNC转入自主控制模式"},
{0x82178217, "开始接近飞行"},
{0x83178317, "开始偏航调姿"},
{0x84178417, "偏航调姿完成"},
{0x85178517, "转平移,靠拢段"},
{0x86178617, "进入停泊点"},
{0x87178717, "开始接近飞行"},
{0x88178817, "开始俯仰调姿"},
{0x89178917, "俯仰调姿完成"},
{0x8A178A17, "最后靠拢飞行"},
{0x8B178B17, NULL}, //预留
{0x8C178C17, NULL}, //预留
{0x8D178D17, "转芬离,撤离段"},
{0x8E178E17, NULL}, // 预留
{0x8F178F17, "开始撤退飞行"},
{0x90179017, "转返回准备段"},
{0x91179117, "开始调姿"},
{0x92179217, NULL}, //预留
{0x93179317, NULL}, //预留
{0x94179417, NULL}, //预留
{0x95179517, "2分钟后服返分离"},
{0x96179617, NULL}, //预留
{0x97179717, NULL}, //预留
{0x98179817, NULL}, //预留
{0x99179917, "2分钟后回收启动"},
{0x9A179A17, "回收开关接通"},
{0x9B179B17, NULL}, //预留
{0x9C179C17, "着陆准备"},
{0x9D179D17, NULL}, //预留
{0x9E179E17, "三十分钟准备"},
{0x9F179F17, "制动发动机开机,指令发出"},
{0xA017A017, "制动发动机关机,指令发出"},
{0xA117A117, "逃逸转入值班"}
};
//获取指令文本与报警码
const char* GetAlarmTextByCode(uint8_t cmd_code[], uint8_t iAlarmCode[4])
{
uint32_t iCode = *(uint32_t*)cmd_code ;
uint32_t* pAlarmCode = (uint32_t*)iAlarmCode;
int n = sizeof(g_AlarmTable) / sizeof(struct AlarmNotice);
for (int i = 0; i<n; ++i) {
if (g_AlarmTable[i].iAlarmCode == iCode) {
*pAlarmCode = iCode;
return g_AlarmTable[i].pAlarmText;
}
}
*pAlarmCode = 0;
printf("0083: no fit code ");
return NULL;
}
/*----*/
void* upper_host_com_thread(void* arg) {//hand-control display processing unit 手控显示处理单元
int tty_id = g_iHostCom_tty_id;
const uint16_t address = COM_FRAME_ADDRESS_VOIX;
const uint32_t buffer_size = 1024 * 100; // TODO
const int64_t iHostAudioBufferSize = 1024 * 1024;
g_pRcvCmdQueue = OpenDataQueue(128, sizeof(struct TelemetryCommandInfo), true);
struct TTSInfo ttsInfo;
uint8_t* buffer = new uint8_t[buffer_size];
int64_t iEnqueueBytes;
bool bSucc;
char* pStrNotice;
AudioPlayInfo PlayInfo;
PlayInfo.m_iUrgent = 2;
//PlayInfo.m_iDataBytes = retLen;
PlayInfo.m_iSource = 0;
PlayInfo.m_iTimerValue = -1;
PlayInfo.m_iTimerID2Set = 0;
size_t offset = 0, cached_size = 0;
uint64_t iTelemetryAnswerTimes;
int16_t* ptrProcHostAudio = (int16_t*)malloc(iHostAudioBufferSize);
int16_t* ptrProcHostAudioEnd = ptrProcHostAudio;
int ProcHostAudioLengthMS = 1000;
int iProcHostAudioSampleCnt;
timeval tLastTime, tCurTime;
TelemetryData4UpperHost UpperHostTeleData;
while (g_bKeepSysRuning) {
ComFrame* frame = ComFrame_ReceiveEx(tty_id, buffer, buffer_size, &offset, &cached_size, &g_bKeepSysRuning, true);
//ComFrame* frame = ComFrame_ReceiveEx(tty_id, buffer, buffer_size, true);
//PrintFilePos(); printf("%04X \n", ComFrame_HEADER(frame)->type);
switch (ComFrame_HEADER(frame)->type)
{
case COM_FRAME_TYPE_COMMAND:
{
//PrintFilePos(); printf("123\n");
// 2024.10.25 disable command frame verify
#ifdef USE_TELE_CTRL_CHECK_SUM
if (ComFrame_Verify(frame, false)) {
PrintFilePos(); fprintf(stderr, "com frame checksum error\n");
// //ComFrame_Del(frame);
// //ComFrame_ReceiveFlushBuffer(ComFrame_LENGTH(frame), buffer, &cached_size);
continue;
}
#endif
if (ComFrame_PAYLOAD_LENGTH(frame) != 6) {
break;
}
struct sysinfo info;
if (sysinfo(&info)) {
fprintf(stderr, "Failed to get sysinfo, errno:%u, reason:%s\n",errno, strerror(errno));
break;
}
uint32_t telemetryTimeSeconds = info.uptime + g_iExternTimeDifference;
g_receiveCommandInfo.seqid++;
g_receiveCommandInfo.code[0] = ComFrame_PAYLOAD(frame)[4];
g_receiveCommandInfo.code[1] = ComFrame_PAYLOAD(frame)[5];
g_receiveCommandInfo.time = telemetryTimeSeconds;
if (!g_bNeedReboot) {
int iRet = EnDataQueue(g_pRcvCmdQueue, &g_receiveCommandInfo, sizeof(TelemetryCommandInfo), NULL, 0, NULL, 0);
if (iRet < sizeof(TelemetryCommandInfo)) {
PrintFilePos(); printf("EnDataQueue failed\n");
}
}
/*-------START 2024.10.23 noted by zgb start
if (is_command(frame->payload, command_open_call, 6)) {
// ProcHostAudioLengthMS = 500;//first audio block at least 200ms
// gettimeofday(&tLastTime, 0);
// SetCallMode(true);
}
else if (is_command(frame->payload, command_close_call, 6)) {
// SetCallMode(false);
}
else if (is_command(frame->payload, command_open_asr, 6)) {
PrintFilePos(); printf("Open Asr\n");
setSysWake("host");
PrintFilePos(); printf("...wakeup by host\n");
g_bCloseASR = false;
SetCallMode(false); //g_bCall = false;
}
else if (is_command(frame->payload, command_close_asr, 6)) {
PrintFilePos(); printf("Close Asr\n");
setSysSleep("by host");
PrintFilePos(); printf("...sleep by host\n");
g_bCloseASR = true;
}
else
//-------END 2024.10.23 noted by zgb
*/
if (is_command(frame->payload, command_wakeup, 6)) {
setSysWake("telemetry");
}
else if(is_command(frame->payload, command_sleep, 6)) {
setSysSleep("telemetry");
}
else if (is_command(frame->payload, command_volume, 6)) {
VolumeUpCycleFunc();
announce_state(0);
}
else if (is_command(frame->payload, command_reset, 6)) {
PrintFilePos(); printf("reboot flag set!\n");
#ifdef USE_TELEMETRY_REBOOT_CMD
g_bNeedReboot = true;// system("reboot");
if(strlen(g_strNoticeCmdText) > 1)
play_text(g_strNoticeCmdText, g_strNoticeCmdTextPlayTimes); // 播报相应内容
#else
if (strlen(g_strNoticeCmdText) > 1)
play_text(g_strNoticeCmdText, g_strNoticeCmdTextPlayTimes); // 播报相应内容
#endif
}
break;
}
case COM_FRAME_TYPE_SPEECH_INJECTED:
{
if (ComFrame_Verify(frame, false)) {
PrintFilePos(); fprintf(stderr, "com frame checksum error\n");
//ComFrame_Del(frame);
//ComFrame_ReceiveFlushBuffer(ComFrame_LENGTH(frame), buffer, &cached_size);
continue;
}
uint8_t payload[6] = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55};
ComFrame* f_data = ComFrame_New(
COM_FRAME_ADDRESS_VOIX,
COM_FRAME_TYPE_SPEECH_INJECTED,
payload, 6, true);
ttsInfo.m_iSource = 0;
ttsInfo.m_iDataBytes = ComFrame_PAYLOAD_LENGTH(frame);
ttsInfo.m_iTimerValue = -1;
g_iOrdinaryPlayStop = 1;
ttsInfo.m_iUrgent = 1;
iEnqueueBytes = EnDataQueue(g_pDataQueue[TEXT_QUEUE_FOR_TTS_URGENT], (void*)(&ttsInfo), sizeof(ttsInfo),
(void*)(frame->payload), ComFrame_PAYLOAD_LENGTH(frame), NULL, 0);
if (iEnqueueBytes <= 0)
{
ProcQueueFull(TEXT_QUEUE_FOR_TTS_URGENT, true);
iEnqueueBytes = EnDataQueue(g_pDataQueue[TEXT_QUEUE_FOR_TTS_URGENT], (void*)(&ttsInfo), sizeof(ttsInfo),
(void*)(frame->payload), ComFrame_PAYLOAD_LENGTH(frame), NULL, 0);
bSucc = false;
}
if (bSucc)
pStrNotice = "proc info-msg";
else
pStrNotice = "***Error: unknow info-msg ";
PrintFilePos(); WriteLog(g_pProcessLog, "%s from %s %s", pStrNotice, "upper-host", g_strCurTime);
if (ComFrame_Send(tty_id, f_data, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
ComFrame_Del(f_data);
break;
}
case COM_FRAME_TYPE_REQUEST:
{
if (ComFrame_Verify(frame, false)) {
PrintFilePos(); fprintf(stderr, "com frame checksum error\n");
continue;
}
need_resend = false;
break;
}
case COM_FRAME_TYPE_AUDIO:
{
//if (ComFrame_Verify(frame, false)) {
// PrintFilePos(); fprintf(stderr, "com frame checksum error\n");
// //ComFrame_Del(frame);
// ComFrame_ReceiveFlushBuffer(ComFrame_LENGTH(frame), buffer, &cached_size);
// continue;
//}
gettimeofday(&tCurTime, 0);
int iIntervalTime = (tCurTime.tv_sec - tLastTime.tv_sec) * 1000 + (tCurTime.tv_usec - tLastTime.tv_usec) / 1000;
if( iIntervalTime > 15 )
PrintFilePos(); WriteLog(g_pProcessLog, " com frame interval %d ms %s\n", iIntervalTime, g_strCurTime);
tLastTime = tCurTime;
if (g_bCallMode) {
iProcHostAudioSampleCnt = proc_audio_upper_host((uint16_t*)(frame->payload), (uint16_t*)ptrProcHostAudioEnd, ComFrame_PAYLOAD_LENGTH(frame) / 2, false);
ptrProcHostAudioEnd += iProcHostAudioSampleCnt;
if (ptrProcHostAudioEnd - ptrProcHostAudio >= 16 * ProcHostAudioLengthMS) {
iEnqueueBytes = EnDataQueue(g_pDataQueue[AUDIO_QUEUE_FOR_PLAY_URGENT], (void*)(&PlayInfo), sizeof(PlayInfo),
(void*)(ptrProcHostAudio), (ptrProcHostAudioEnd - ptrProcHostAudio) * 2, NULL, 0);// 16 * ProcHostAudioLengthMS * 2, NULL, 0);
if (iEnqueueBytes <= 0) {
ProcQueueFull(AUDIO_QUEUE_FOR_PLAY_URGENT, true);
iEnqueueBytes = EnDataQueue(g_pDataQueue[AUDIO_QUEUE_FOR_PLAY_URGENT], (void*)(&PlayInfo), sizeof(PlayInfo),
(void*)(ptrProcHostAudio), (ptrProcHostAudioEnd - ptrProcHostAudio) * 2, NULL, 0);// 16 * ProcHostAudioLengthMS * 2, NULL, 0);
}
//memcpy(ptrProcHostAudio, ptrProcHostAudio + 16 * ProcHostAudioLengthMS, (ptrProcHostAudioEnd - ptrProcHostAudio - 16 * ProcHostAudioLengthMS) * 2);
ptrProcHostAudioEnd = ptrProcHostAudio;// ptrProcHostAudioEnd -= ptrProcHostAudio;
ProcHostAudioLengthMS = 500;//restore to 100ms
}
//iEnqueueBytes = EnDataQueue(g_pDataQueue[AUDIO_QUEUE_FOR_PLAY_URGENT], (void*)(&PlayInfo), sizeof(PlayInfo),
// (void*)(frame->payload), ComFrame_PAYLOAD_LENGTH(frame), NULL, 0);
//if (iEnqueueBytes <= 0) {
// ProcQueueFull(AUDIO_QUEUE_FOR_PLAY_URGENT, true);
// iEnqueueBytes = EnDataQueue(g_pDataQueue[AUDIO_QUEUE_FOR_PLAY_URGENT], (void*)(&PlayInfo), sizeof(PlayInfo),
// (void*)(frame->payload), ComFrame_PAYLOAD_LENGTH(frame), NULL, 0);
//}
//PrintFilePos(); printf("iProcHostAudioSampleCnt %d\n", iProcHostAudioSampleCnt);
}
break;
}
case COM_FRAME_TYPE_TELEMETRY_REQUEST:
{
if (g_iUseHostComForTelemetry != 1) {
break;
}
if (ComFrame_Verify(frame, false)) {
if (SendTelemetryErrorMsg(tty_id, address, TELEMETRY_ERROR_CHECKSUM, true))
PrintFilePos(); fprintf(stderr, "send telemetry exception frame error: %s\n", strerror(errno));
continue;
}
if (frame->payload[0] != 0xFF || frame->payload[1] != 0x11) {
if (SendTelemetryErrorMsg(tty_id, address, TELEMETRY_ERROR_TYPE, true))
PrintFilePos(); fprintf(stderr, "send telemetry exception frame error: %s\n", strerror(errno));
continue;
}
if (ComFrame_HEADER(frame)->type != COM_FRAME_TYPE_TELEMETRY_REQUEST ||
ComFrame_PAYLOAD_LENGTH(frame) != 2) {
continue;
}
set_telemetry_host_data(&UpperHostTeleData); // count from 0, 1, ..
ComFrame* pUpperHostTeleAnswerMsg = NewTelemetryAnswerData(address, &UpperHostTeleData, true);
if (ComFrame_Send(tty_id, pUpperHostTeleAnswerMsg, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
// added by gejp start
// if (telemetry_3package_loop_count[1] == 0) {
// telemetry_3package_count[1] += 1;
// }
// telemetry_3package_loop_count[1] += 1;
// if (telemetry_3package_loop_count[1] == 3) {
// telemetry_3package_loop_count[1] = 0;
// }
// added by gejp end
ComFrame_Del(pUpperHostTeleAnswerMsg);
break;
}
case COM_FRAME_TYPE_VOICE_ANNOUNCEMENT_AND_ALARM_CODE_REQUEST: // gejp 2024.10.03
{
if (g_iEnableAlarmCode == 0) {
continue;
}
if (ComFrame_Verify(frame, false)) {
PrintFilePos(); fprintf(stderr, "com frame checksum error\n");
continue;
}
if (frame->payload[0] != 0xAA || frame->payload[1] != 0x55) { // data error
// printf("alarm code: data error");
continue;
}
int64_t iEnqueueBytes;
int iTextBytes;
const char *pNoticeStr;
ttsInfo.m_iSource = 0;
ttsInfo.m_iTimerValue = -1;
g_iOrdinaryPlayStop = 1;
ttsInfo.m_iUrgent = 1;
//语音通报与报警码
pNoticeStr = GetAlarmTextByCode(frame->payload+2, g_iAlarmCode);
if(!pNoticeStr)
continue;
iTextBytes = strlen(pNoticeStr) + 1;
ttsInfo.m_iDataBytes = iTextBytes;
iEnqueueBytes = EnDataQueue(g_pDataQueue[TEXT_QUEUE_FOR_TTS_URGENT], (void*)(&ttsInfo), sizeof(ttsInfo),
(void*)pNoticeStr, iTextBytes, NULL, 0);
if (iEnqueueBytes <= 0) {//队列满,入队失败
ProcQueueFull(TEXT_QUEUE_FOR_TTS_URGENT, true);
iEnqueueBytes = EnDataQueue(g_pDataQueue[TEXT_QUEUE_FOR_TTS_URGENT], (void*)(&ttsInfo), sizeof(ttsInfo),
(void*)pNoticeStr, iTextBytes, NULL, 0);
}
uint8_t payload[6] = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55};
ComFrame* f_data = ComFrame_New(
COM_FRAME_ADDRESS_VOIX,
COM_FRAME_TYPE_VOICE_ANNOUNCEMENT_AND_ALARM_CODE_RESPONSE,
payload, 6, true);
if (ComFrame_Send(tty_id, f_data, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
ComFrame_Del(f_data);
break;
}
case COM_FRAME_TYPE_GRANT_TIME: { // gejp 2024.10.03
if (ComFrame_Verify(frame, false)) {
PrintFilePos(); fprintf(stderr, "com frame checksum error\n");
continue;
}
if (frame->payload[0] != 0x66) { // data error
printf("grant time: data error");
continue;
}
int secondsAbs = *((int*)(frame->payload+1));
secondsAbs = byteswapl(secondsAbs);
struct sysinfo info;
if (sysinfo(&info)) {
fprintf(stderr, "Failed to get sysinfo, errno:%u, reason:%s\n",errno, strerror(errno));
break;
}
PrintFilePos(); printf("---> uptime changed\n");
PrintFilePos(); printf("old uptime: %d\n", info.uptime + g_iExternTimeDifference);
g_iExternTimeDifference = secondsAbs - info.uptime;
PrintFilePos(); printf("new uptime: %d\n", info.uptime + g_iExternTimeDifference);
break;
};
default:
break;
}
}
delete[] buffer;
free(ptrProcHostAudio);
if (g_pRcvCmdQueue)
{
CloseDataQueue(g_pRcvCmdQueue);
g_pRcvCmdQueue = NULL;
}
return NULL;
}
void send_command_upper_host(int length, void* payload) {
int tty_id = g_iHostCom_tty_id;
need_resend = true;
int sendtime = 0;
ComFrame* f_data = ComFrame_New(
COM_FRAME_ADDRESS_VOIX,
COM_FRAME_TYPE_REQUEST,
payload, length, true);
uint64_t last_send_time;
while (need_resend) {
if (sendtime == 0) {
last_send_time = g_iSysTimerMilliSeconds;
if (ComFrame_Send(tty_id, f_data, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
sendtime += 1;
}
else if (sendtime == 3) {
break;
}
else {
if (g_iSysTimerMilliSeconds > last_send_time + 250) {
last_send_time = g_iSysTimerMilliSeconds;
if (ComFrame_Send(tty_id, f_data, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
sendtime += 1;
}
else {
usleep(500);
}
}
}
ComFrame_Del(f_data);
if (length != 6) return;
uint16_t send_seq = (g_sendCommandList.empty()) ? 0 : g_sendCommandList.front().seqid;
struct sysinfo info;
if (sysinfo(&info)) {
fprintf(stderr, "Failed to get sysinfo, errno:%u, reason:%s\n",errno, strerror(errno));
return;
}
uint32_t telemetryTimeSeconds = info.uptime + g_iExternTimeDifference;
TelemetryCommandInfo telemetryCommandInfo = {telemetryTimeSeconds, {((uint8_t*)payload)[3], ((uint8_t*)payload)[4]}, send_seq + 1};
g_sendCommandList.push_front(telemetryCommandInfo);
if (g_sendCommandList.size() > TELEMETRY_SEND_COMMAND_INFO_LENGTH) {
g_sendCommandList.pop_back();
}
}
void send_audio_upper_host(int length, void* payload) {
int tty_id = g_iHostCom_tty_id;
ComFrame* f_data = ComFrame_New(
COM_FRAME_ADDRESS_VOIX,
COM_FRAME_TYPE_AUDIO,
payload, length, true);
if (ComFrame_Send(tty_id, f_data, true)) {
PrintFilePos(); fprintf(stderr, "send telemetry data frame error: %s\n", strerror(errno));
}
ComFrame_Del(f_data);
}

256
tests/c_testcase.cpp Normal file
View File

@ -0,0 +1,256 @@
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>
#include <sstream>
#include <atomic>
#include <signal.h>
#include <setjmp.h>
#include "c_testcase.h"
using namespace std;
#define TEST_CASE_STATUS_PASSED 0
#define TEST_CASE_STATUS_SKIPPED 32
#define TEST_CASE_STATUS_FAILED 16
#define TEST_CASE_STATUS_SETUP_FAILED 17
#define TEST_CASE_STATUS_TEARDOWN_FAILED 18
#define MAX_TESTCASE 64
struct TestCase {
const char* name;
test_case func;
};
static TestCase test_cases[MAX_TESTCASE];
static int test_case_total = 0;
static interactive_func interactive = NULL;
static context_func setup = NULL;
static context_func teardown = NULL;
static atomic_bool testcase_running = false;
static jmp_buf testcase_env;
static int testcase_exit_code = 0;
int _add_test_case(const char* name, test_case func) {
if (test_case_total == MAX_TESTCASE) {
fprintf(stderr, "too many test case\n");
exit(1);
}
TestCase* tc = &(test_cases[test_case_total++]);
tc->name = name;
tc->func = func;
return 0;
}
int _set_interactive(interactive_func func) {
interactive = func;
return 0;
}
int _set_setup(context_func func) {
setup = func;
return 0;
}
int _set_teardown(context_func func) {
teardown = func;
return 0;
}
void __attribute__((noreturn)) test_case_abort(int exit_code) {
if (!testcase_running.load(std::memory_order_acquire)) {
exit(exit_code);
}
testcase_exit_code = exit_code;
longjmp(testcase_env, 1);
}
static __inline int get_tty_col(int fd) {
struct winsize size;
ioctl(fd, TIOCGWINSZ,&size);
return size.ws_col;
}
static __inline void print_separator(char lc) {
int size = get_tty_col(STDOUT_FILENO);
for (int i = 0; i < size; ++i) {
putchar(lc);
}
}
static __inline void print_separator_ex(char lc, const char* str, const char* color) {
int size = get_tty_col(STDOUT_FILENO);
int len = strlen(str);
printf("\033[0m%s", color); // 设置颜色
if(len > size) {
printf("%s\n", str);
} else {
int pad = (size - len - 2) / 2;
for(int i = 0; i < pad; i++) {
putchar(lc);
}
printf(" %s ", str);
for(int i = 0; i < pad; i++) {
putchar(lc);
}
if((size - len) % 2) putchar(lc);
putchar('\n');
}
printf("\033[0m"); // 重置颜色
}
static int collect_testcase() {
for (int i = 0; i < test_case_total; ++i) {
puts(test_cases[i].name);
putchar(' ');
}
putchar('\n');
return 0;
}
static TestCase* get_test_case(const char* name) {
TestCase* tc = NULL;
if (*name >= '0' && *name <= '9') {
int id = atoi(name);
if (id >= 0 && id < test_case_total) {
tc = &(test_cases[id]);
}
} else {
for (int i = 0; i < test_case_total; ++i) {
if (strcmp(test_cases[i].name, name) == 0) {
tc = &(test_cases[i]);
break;
}
}
}
return tc;
}
static int run_test_case_func(test_case func) {
bool running = false;
if (!testcase_running.compare_exchange_strong(running, true, std::memory_order_acq_rel)) {
cerr << "test case is running" << endl;
return 1;
}
if (setjmp(testcase_env)) {
return testcase_exit_code;
}
int ret = func();
running = true;
if (!testcase_running.compare_exchange_strong(running, false, std::memory_order_acq_rel)) {
cerr << "test case is not running" << endl;
return 1;
}
return ret;
}
static int unittest_testcase(TestCase* tc) {
assert(tc != NULL);
if (setup && setup(tc->name)) {
return TEST_CASE_STATUS_SETUP_FAILED;
}
int ret = run_test_case_func(tc->func);
if (teardown && teardown(tc->name)) {
return TEST_CASE_STATUS_TEARDOWN_FAILED;
}
if (ret == SKIP_RET_NUMBER) {
return TEST_CASE_STATUS_SKIPPED;
} else if (ret != 0) {
return TEST_CASE_STATUS_FAILED;
} else {
return 0;
}
}
int main(int argc, const char** argv) {
for (int i = 1; i < argc; ++i) {
const char* arg = argv[i];
if (strcmp(arg, "-i") == 0 || strcmp(arg, "--interactive") == 0) {
if (interactive)
return interactive(argc, argv);
else {
cout << "interactive mode is not supported" << endl;
return 1;
}
}
else if (strcmp(arg, "-c") == 0 || strcmp(arg, "--collect") == 0) {
return collect_testcase();
}
else if (strcmp(arg, "-u") == 0 || strcmp(arg, "--unittest") == 0) {
const char* name = NULL;
if (i + 1 < argc) {
name = argv[++i];
} else {
cout << "--unittest require an argument" << endl;
return 2;
}
TestCase* tc = get_test_case(name);
if (tc == NULL) {
cout << "test case " << name << " not found" << endl;
return 1;
}
return unittest_testcase(tc);
} else {
if (interactive)
return interactive(argc, argv);
else {
cout << "unknown argument '" << arg << "'" << endl;
return 1;
}
}
}
int total = test_case_total;
int passed = 0;
int failed = 0;
int skipped = 0;
for (int i = 0; i < total; ++i) {
print_separator('-');
TestCase* tc = &test_cases[i];
cout << "running " << tc->name << endl;
switch (unittest_testcase(tc)) {
case 0:
cout << "\033[0m\033[1;32mtest case \"" << tc->name << "\" passed\033[0m" << endl;
passed++;
break;
case TEST_CASE_STATUS_SKIPPED:
cout << "\033[0m\033[1;33mtest case \"" << tc->name << "\" skipped\033[0m" << endl;
skipped++;
break;
case TEST_CASE_STATUS_SETUP_FAILED:
cout << "\033[0m\033[1;31msetup \"" << tc->name << "\" failed\033[0m" << endl;
failed++;
break;
case TEST_CASE_STATUS_TEARDOWN_FAILED:
cout << "\033[0m\033[1;31mteardown \"" << tc->name << "\" failed\033[0m" << endl;
failed++;
break;
default:
cout << "\033[0m\033[1;31mtest case \"" << tc->name << "\" failed\033[0m" << endl;
failed++;
break;
}
}
stringstream ss;
ss << "total: " << total << ", passed: " << passed << ", failed: " << failed << ", skipped: " << skipped;
string sum = ss.str();
const char* color;
if (failed)
color = "\033[1;31m";
else if (skipped)
color = "\033[1;33m";
else
color = "\033[1;32m";
print_separator_ex('=', sum.c_str(), color);
return 0;
}