commit 9bf7306aa2e6ce962a146164fd01148873e09c10 Author: ovizro Date: Thu Nov 28 16:31:00 2024 +0800 init repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa94f9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +################### CMake config ################### + +build + +################ Executable program ################ + +bin/ +dist/ + +################### VSCode config ################## + +.vscode +.VSCodeCounter + +####################### Tools ###################### + +tools/* + +#################### Test config ################### + +test.* +disabled.* +*.disabled + +###################### Misc ####################### + +!.gitkeep diff --git a/docs/extern_interface.md b/docs/extern_interface.md new file mode 100644 index 0000000..24ba56a --- /dev/null +++ b/docs/extern_interface.md @@ -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*)`:授时回调函数 diff --git a/include/c_testcase.h b/include/c_testcase.h new file mode 100644 index 0000000..017245f --- /dev/null +++ b/include/c_testcase.h @@ -0,0 +1,173 @@ +#ifndef _INCLUDE_C_TESTCASE_ +#define _INCLUDE_C_TESTCASE_ + +#include +#include +#include + +#ifdef __cplusplus +#include + +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 (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 diff --git a/include/comframe.h b/include/comframe.h new file mode 100755 index 0000000..725aed4 --- /dev/null +++ b/include/comframe.h @@ -0,0 +1,240 @@ +#ifndef _INCLUDE_FRAME_ +#define _INCLUDE_FRAME_ + +#include +#include +#include +#include +#include +#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 diff --git a/include/command.h b/include/command.h new file mode 100755 index 0000000..3340111 --- /dev/null +++ b/include/command.h @@ -0,0 +1,20 @@ +#ifndef SYS_COMMAND_H_ +#define SYS_COMMAND_H_ + +#include +#include + +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 diff --git a/include/dataqueue.hpp b/include/dataqueue.hpp new file mode 100644 index 0000000..c742127 --- /dev/null +++ b/include/dataqueue.hpp @@ -0,0 +1,58 @@ +#ifndef _INCLUDED_QUEUE_ +#define _INCLUDED_QUEUE_ + +#include +#include +#include + +template +class DataQueue { +public: + DataQueue(size_t init_size = 0) : m_Queue(init_size) + {} + DataQueue(DataQueue&) = delete; + + void Push(T data) + { + std::lock_guard lock(m_Mutex); + m_Queue.push_back(data); + m_Cond.notify_one(); + } + + T Pop() + { + std::unique_lock 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 lock(m_Mutex); + return m_Queue.empty(); + } + + size_t Size() + { + std::lock_guard lock(m_Mutex); + return m_Queue.size(); + } + + void Clear() + { + std::lock_guard lock(m_Mutex); + m_Queue.clear(); + } + +protected: + std::list m_Queue; + std::mutex m_Mutex; + std::condition_variable m_Cond; +}; + +#endif \ No newline at end of file diff --git a/include/defines.h b/include/defines.h new file mode 100755 index 0000000..4881a17 --- /dev/null +++ b/include/defines.h @@ -0,0 +1,255 @@ +#ifndef VOIX_DEFINES_H +#define VOIX_DEFINES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 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 g_iOrdinaryPlayStop; + +extern bool g_bVolumeKeyState; +extern bool g_bWakeupKeyState; +extern bool g_bVolumeKeyPressed; +extern bool g_bWakeupKeyPressed; + +#endif //AUDIOSERVER_AUDIODATAUNIT_H diff --git a/include/event.h b/include/event.h new file mode 100644 index 0000000..59cdccb --- /dev/null +++ b/include/event.h @@ -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 \ No newline at end of file diff --git a/include/extern_interface.h b/include/extern_interface.h new file mode 100644 index 0000000..a129e17 --- /dev/null +++ b/include/extern_interface.h @@ -0,0 +1,49 @@ +#ifndef _INCLUDE_EXTERN_INTERFACE_H_ +#define _INCLUDE_EXTERN_INTERFACE_H_ + +#include +#include +#include + +#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 \ No newline at end of file diff --git a/include/telemetry.h b/include/telemetry.h new file mode 100755 index 0000000..bbe4904 --- /dev/null +++ b/include/telemetry.h @@ -0,0 +1,196 @@ +#ifndef _INCLUDE_TELEMETRY_ +#define _INCLUDE_TELEMETRY_ + +#include +#include +#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 diff --git a/logqueueconfig/CCfgFileParser.cpp b/logqueueconfig/CCfgFileParser.cpp new file mode 100755 index 0000000..4f93494 --- /dev/null +++ b/logqueueconfig/CCfgFileParser.cpp @@ -0,0 +1,263 @@ +//bb.cpp + +#include +#include +#include +#include +#include +#include +#include + +#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 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 §ion = (*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 &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; +} \ No newline at end of file diff --git a/logqueueconfig/CCfgFileParser.h b/logqueueconfig/CCfgFileParser.h new file mode 100755 index 0000000..5eb141e --- /dev/null +++ b/logqueueconfig/CCfgFileParser.h @@ -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 +#include + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include +#include + +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 SECTION_CONTENT; + typedef std::map 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& 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_) diff --git a/logqueueconfig/Makefile b/logqueueconfig/Makefile new file mode 100755 index 0000000..9f43a67 --- /dev/null +++ b/logqueueconfig/Makefile @@ -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) diff --git a/logqueueconfig/backup b/logqueueconfig/backup new file mode 100755 index 0000000..fbd5e41 --- /dev/null +++ b/logqueueconfig/backup @@ -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} + + diff --git a/logqueueconfig/dataqueue.cpp b/logqueueconfig/dataqueue.cpp new file mode 100755 index 0000000..b43ad56 --- /dev/null +++ b/logqueueconfig/dataqueue.cpp @@ -0,0 +1,537 @@ +//#pragma once +#include +#include "dataqueue.h" +#include + +#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����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�ı��� + + 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); //�ճ�4�� + // 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; +} + + + diff --git a/logqueueconfig/dataqueue.h b/logqueueconfig/dataqueue.h new file mode 100755 index 0000000..855163d --- /dev/null +++ b/logqueueconfig/dataqueue.h @@ -0,0 +1,78 @@ +//#pragma once +#ifndef __HIT_DATAQUEUE_H +#define __HIT_DATAQUEUE_H +#include +#include +#include +#include +#include + +#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 diff --git a/logqueueconfig/log.cpp b/logqueueconfig/log.cpp new file mode 100755 index 0000000..680407b --- /dev/null +++ b/logqueueconfig/log.cpp @@ -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; +} diff --git a/logqueueconfig/log.h b/logqueueconfig/log.h new file mode 100755 index 0000000..76446f6 --- /dev/null +++ b/logqueueconfig/log.h @@ -0,0 +1,287 @@ +#ifndef __HIT_LOG_HEADER +#define __HIT_LOG_HEADER +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 diff --git a/logqueueconfig/mak b/logqueueconfig/mak new file mode 100755 index 0000000..5b87ec7 --- /dev/null +++ b/logqueueconfig/mak @@ -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 + diff --git a/mak b/mak new file mode 100755 index 0000000..df17b1d --- /dev/null +++ b/mak @@ -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 -- diff --git a/scripts/unittest.py b/scripts/unittest.py new file mode 100644 index 0000000..8f3f416 --- /dev/null +++ b/scripts/unittest.py @@ -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"" + + 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) diff --git a/scripts/unittest.sh b/scripts/unittest.sh new file mode 100644 index 0000000..f02157c --- /dev/null +++ b/scripts/unittest.sh @@ -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 ' ' '*' diff --git a/src/comframe.cpp b/src/comframe.cpp new file mode 100755 index 0000000..1ae8940 --- /dev/null +++ b/src/comframe.cpp @@ -0,0 +1,327 @@ +// #define COM_FRAME_DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/src/event.cpp b/src/event.cpp new file mode 100644 index 0000000..49fc4b6 --- /dev/null +++ b/src/event.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include "event.h" +#include "dataqueue.hpp" + +std::unordered_map>> _events; +bool _event_thread_running = false; +DataQueue> _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; +} diff --git a/src/extern_interface.cpp b/src/extern_interface.cpp new file mode 100644 index 0000000..c643d5f --- /dev/null +++ b/src/extern_interface.cpp @@ -0,0 +1,3 @@ +#include "extern_interface.h" + + diff --git a/src/host_com.cpp b/src/host_com.cpp new file mode 100644 index 0000000..35bc0d9 --- /dev/null +++ b/src/host_com.cpp @@ -0,0 +1,807 @@ +#include +#include +#include +#include +#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 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; itype); + 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); +} diff --git a/tests/c_testcase.cpp b/tests/c_testcase.cpp new file mode 100644 index 0000000..5535f69 --- /dev/null +++ b/tests/c_testcase.cpp @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file