diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8a7f665 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 3.15) + +project(extern_interface CXX) + +set(CMAKE_CXX_STANDARD 11) +if (NOT CMAKE_CROSSCOMPILING) + set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) +endif() + +if (UNIX) + set(CMAKE_CXX_FLAGS "-std=c++17 -fPIC -Wall ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "-g ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_RELEASE "-g -O2 ${CMAKE_CXX_FLAGS}") +elseif (WIN32) + # windows platform + #add_definitions(-D_CRT_SECURE_NO_WARNINGS) + #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") + #set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /EHsc") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /EHsc") +endif() + +if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") +endif() + +if (CMAKE_CROSSCOMPILING) + message(STATUS "Cross compiling ...") + message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") + message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +include_directories(./include) + +aux_source_directory(./src SRC_LIST) + +add_library(extern_interface_static STATIC ${SRC_LIST}) +add_library(extern_interface SHARED ${SRC_LIST}) + +# 设置测试文件和公共源文件 +set(TESTS_DIR "${CMAKE_SOURCE_DIR}/tests") +set(TEST_COMMON_SOURCE "${TESTS_DIR}/c_testcase.cpp") + +# 查找所有以 test_ 开头的文件 +file(GLOB_RECURSE TEST_FILES "${TESTS_DIR}/test_*.cpp") + +# 循环遍历每个测试文件并创建可执行文件 +foreach(TEST_FILE ${TEST_FILES}) + # 获取文件名(不带路径和扩展名) + get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) + + # 创建可执行文件 + add_executable(${TEST_NAME} ${TEST_FILE} ${TEST_COMMON_SOURCE} ${SRC_LIST}) + + # 将可执行文件路径添加到列表中 + list(APPEND TEST_EXECUTABLES "${EXECUTABLE_OUTPUT_PATH}/${TEST_NAME}") +endforeach() + +enable_testing() + +add_test(NAME test + COMMAND ${CMAKE_SOURCE_DIR}/scripts/unittest.py ${TEST_EXECUTABLES} +) diff --git a/include/comframe.h b/include/comframe.h index 725aed4..ee51943 100755 --- a/include/comframe.h +++ b/include/comframe.h @@ -6,9 +6,30 @@ #include #include #include -#include "defines.h" + //#define COM_FRAME_DEBUG +#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 byteswapl(x) (0xff000000 & x << 24) \ |(0x00ff0000 & x << 8) \ |(0x0000ff00 & x >> 8) \ @@ -230,8 +251,8 @@ ComFrame* ComFrame_Receive(int tty_id, const size_t buffer_size, const bool swap /// @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); +int get_com_tty_id(const char* path, unsigned int baudRate); +void init_tty(int tty_id, unsigned int baudRate); #ifdef __cplusplus } diff --git a/include/defines.h b/include/defines.h index 4881a17..b636dae 100755 --- a/include/defines.h +++ b/include/defines.h @@ -10,9 +10,6 @@ #include #include #include -#include "CCfgFileParser.h" -#include "dataqueue.h" -#include "log.h" using namespace std; #define PrintFilePos() //printf("\nfile:%s, line:%2d---",__FILE__,__LINE__); @@ -33,26 +30,7 @@ using namespace std; //-------------------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 diff --git a/include/extern_interface.h b/include/extern_interface.h index be2e6f7..3ecfc25 100644 --- a/include/extern_interface.h +++ b/include/extern_interface.h @@ -46,7 +46,7 @@ union PacketData typedef void (*et_callback_t)(uint32_t, size_t, union PacketData); EXTERN_INTERFACE_PUBLIC -void extern_interface_init(const char* config_path, et_callback_t cb); +int extern_interface_init(const char* config_path, et_callback_t cb); EXTERN_INTERFACE_PUBLIC void extern_interface_send(uint32_t type, size_t len, union PacketData data); diff --git a/include/inicpp.hpp b/include/inicpp.hpp new file mode 100644 index 0000000..f3cd06e --- /dev/null +++ b/include/inicpp.hpp @@ -0,0 +1,687 @@ +/* + * MIT License + * + * Copyright (c) 2023 dujingning + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __JN_INICPP_H__ +#define __JN_INICPP_H__ + +#include + +#include +#include +#include +#include +#include + +// for std::string <==> std::wstring convert +#include +#include + +#ifdef INICPP_DEBUG + +#include +#include +#include + +class TimeFormatter +{ +public: + static std::string format(const std::string &format = "%Y-%m-%d %H:%M:%S") + { + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + std::array buffer; + std::strftime(buffer.data(), buffer.size(), format.c_str(), &tm); + return buffer.data(); + } +}; + +#define CODE_INFO std::string(" | Code:\'file:") + std::string(__FILE__) + ",function:" + std::string(__FUNCTION__) + ",line:" + std::to_string(__LINE__) + '\'' +#define INI_DEBUG(x) std::cout << "INICPP " << TimeFormatter::format() << " : " << x << CODE_INFO << std::endl + +#else // #ifdef INICPP_DEBUG +#define INI_DEBUG(x) +#endif // #ifdef INICPP_DEBUG + +namespace inicpp +{ + + typedef struct KeyValueNode + { + std::string Value = ""; + int lineNumber = -1; // text line start with 1 + } KeyValueNode; + + class section + { + public: + section() : _sectionName() + { + } + + explicit section(const std::string §ionName) : _sectionName(sectionName) + { + } + + const std::string &name() + { + return _sectionName; + } + + const std::string getValue(const std::string &Key) + { + if (!_sectionMap.count(Key)) + { + return ""; + } + return _sectionMap[Key].Value; + } + + void setName(const std::string &name, const int &lineNumber) + { + _sectionName = name; + _lineNumber = lineNumber; + } + + void setValue(const std::string &Key, const std::string &Value, const int line) + { + _sectionMap[Key].Value = Value; + _sectionMap[Key].lineNumber = line; + } + + void append(section &sec) + { + _sectionMap.insert(sec._sectionMap.begin(), sec._sectionMap.end()); + } + + bool isKeyExist(const std::string &Key) + { + return !_sectionMap.count(Key) ? false : true; + } + + int getEndSection() + { + int line = -1; + + if (_sectionMap.empty() && _sectionName != "") + { + return _lineNumber; + } + + for (const auto &data : _sectionMap) + { + if (data.second.lineNumber > line) + { + line = data.second.lineNumber; + } + } + return line; + } + + int getLine(const std::string &Key) + { + if (!_sectionMap.count(Key)) + { + return -1; + } + return _sectionMap[Key].lineNumber; + } + + const std::string operator[](const std::string &Key) + { + if (!_sectionMap.count(Key)) + { + return ""; + } + return _sectionMap[Key].Value; + } + + void clear() + { + _lineNumber = -1; + _sectionName.clear(); + _sectionMap.clear(); + } + + bool isEmpty() const + { + return _sectionMap.empty(); + } + + int toInt(const std::string &Key) + { + if (!_sectionMap.count(Key)) + { + return 0; + } + + int result = 0; + + try + { + result = std::stoi(_sectionMap[Key].Value); + } + catch (const std::invalid_argument &e) + { + INI_DEBUG("Invalid argument: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'"); + } + catch (const std::out_of_range &e) + { + INI_DEBUG("Out of range: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'"); + } + + return result; + } + + std::string toString(const std::string &Key) + { + if (!_sectionMap.count(Key)) + { + return ""; + } + return _sectionMap[Key].Value; + } + + std::wstring toWString(const std::string &Key) + { + std::wstring_convert> converter; + return converter.from_bytes(toString(Key)); + } + + double toDouble(const std::string &Key) + { + if (!_sectionMap.count(Key)) + { + return 0.0; + } + + double result = 0.0; + + try + { + result = std::stod(_sectionMap[Key].Value); + } + catch (const std::invalid_argument &e) + { + INI_DEBUG("Invalid argument: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'"); + } + catch (const std::out_of_range &e) + { + INI_DEBUG("Out of range: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'"); + } + + return result; + } + + // todo : add toString() / toInt() / toDouble() + + private: + std::string _sectionName; + std::map _sectionMap; + int _lineNumber = -1; // text line start with 1 + }; + + class ini + { + public: + void addSection(section &sec) + { + if (_iniInfoMap.count(sec.name())) // if exist,need to merge + { + _iniInfoMap[sec.name()].append(sec); + return; + } + _iniInfoMap.emplace(sec.name(), sec); + return; + } + + void removeSection(const std::string §ionName) + { + if (!_iniInfoMap.count(sectionName)) + { + return; + } + _iniInfoMap.erase(sectionName); + return; + } + + bool isSectionExists(const std::string §ionName) + { + return !_iniInfoMap.count(sectionName) ? false : true; + } + + // may contains default of Unnamed section with "" + std::list getSectionsList() + { + std::list sectionList; + for (const auto &data : _iniInfoMap) + { + if (data.first == "" && data.second.isEmpty()) // default section: no section name,if empty,not count it. + { + continue; + } + sectionList.emplace_back(data.first); + } + return sectionList; + } + + const section &operator[](const std::string §ionName) + { + if (!_iniInfoMap.count(sectionName)) + { + return _iniInfoMap[""]; + } + return _iniInfoMap[sectionName]; + } + + inline std::size_t getSectionSize() + { + return _iniInfoMap.size(); + } + + std::string getValue(const std::string §ionName, const std::string &Key) + { + if (!_iniInfoMap.count(sectionName)) + { + return ""; + } + return _iniInfoMap[sectionName][Key]; + } + + // for none section + int getLine(const std::string &Key) + { + if (!_iniInfoMap.count("")) + { + return -1; + } + return _iniInfoMap[""].getLine(Key); + } + + // for section-key + int getLine(const std::string §ionName, const std::string &Key) + { + if (!_iniInfoMap.count(sectionName)) + { + return -1; + } + return _iniInfoMap[sectionName].getLine(Key); + } + + inline void clear() { _iniInfoMap.clear(); } + inline bool empty() { return _iniInfoMap.empty(); } + + protected: + std::map _iniInfoMap; + }; + + // todo if file is modified,never write back + class IniManager + { + public: + explicit IniManager(const std::string &configFileName) : _configFileName(configFileName) + { + parse(); + } + + ~IniManager() + { + _iniFile.close(); + } + + section operator[](const std::string §ionName) + { + return _iniData[sectionName]; + } + + void parse() + { + _iniFile.open(_configFileName, std::ifstream::in | std::ifstream::out | std::fstream::app); + if (!_iniFile.is_open()) + { + INI_DEBUG("Failed to open the input INI file for parsing!"); + return; + } + + _iniData.clear(); + + _iniFile.seekg(0, _iniFile.beg); + std::string data, sectionName; + int sectionLine = -1; + + section sectionRecord; + + _SumOfLines = 1; + do + { + std::getline(_iniFile, data); + + if (!filterData(data)) + { + ++_SumOfLines; + continue; + } + + if (data.find('[') == 0) // section + { + if (!sectionRecord.isEmpty() || sectionRecord.name() != "") + { + _iniData.addSection(sectionRecord); + } + + size_t first = data.find('['); + size_t last = data.find(']'); + + if (last == std::string::npos) + { + ++_SumOfLines; + continue; + } + + sectionName = data.substr(first + 1, last - first - 1); + sectionLine = _SumOfLines; + + sectionRecord.clear(); + sectionRecord.setName(sectionName, sectionLine); + } + + size_t pos = data.find('='); + if (pos != std::string::npos) + { // k=v + std::string key = data.substr(0, pos); + std::string value = data.substr(pos + 1); + + trimEdges(key); + trimEdges(value); + + sectionRecord.setValue(key, value, _SumOfLines); + } + + ++_SumOfLines; + + } while (!_iniFile.eof()); + + if (!sectionRecord.isEmpty()) + { + sectionRecord.setName(sectionName, -1); + _iniData.addSection(sectionRecord); + } + + if (_iniFile.is_open()) + { + _iniFile.close(); + } + } + + bool modify(const std::string &Section, const std::string &Key, const std::string &Value, const std::string &comment = "") + { // todo: insert comment before k=v + parse(); + + if (Key == "" || Value == "") + { + INI_DEBUG("Invalid parameter input: Key[" << Key << "],Value[" << Value << "]"); + return false; + } + + std::string keyValueData = Key + "=" + Value + "\n"; + if (comment.length() > 0) + { + keyValueData = comment + "\n" + keyValueData; + if (comment[0] != ';') + { + keyValueData = ";" + keyValueData; + } + } + + const std::string &tempFile = ".temp.ini"; + std::fstream input(_configFileName, std::ifstream::in | std::ifstream::out | std::fstream::app); + std::ofstream output(tempFile); + + if (!input.is_open()) + { + INI_DEBUG("Failed to open the input INI file for modification!"); + return false; + } + + if (!output.is_open()) + { + INI_DEBUG("Failed to open the input INI file for modification!"); + return false; + } + + int line_number_mark = -1; + bool isInputDataWited = false; + + do + { + // exist key at one section replace it, or need to create it + if (_iniData.isSectionExists(Section)) + { + line_number_mark = (*this)[Section].getLine(Key); + + if (line_number_mark == -1) + { // section exist, key not exist + line_number_mark = (*this)[Section].getEndSection(); + + std::string lineData; + int input_line_number = 0; + while (std::getline(input, lineData)) + { + ++input_line_number; + + if (input_line_number == (line_number_mark + 1)) + { // new line,append to next line + isInputDataWited = true; + output << keyValueData; + } + + output << lineData << "\n"; + } + + if (input.eof() && !isInputDataWited) + { + isInputDataWited = true; + output << keyValueData; + } + + break; + } + } + + if (line_number_mark <= 0) // not found key at config file + { + input.seekg(0, input.beg); + + bool isHoldSection = false; + std::string newLine = "\n\n"; + if (Section != "" && Section.find("[") == std::string::npos && Section.find("]") == std::string::npos && Section.find("=") == std::string::npos) + { + if (_iniData.empty() || _iniData.getSectionSize() <= 0) + { + newLine.clear(); + } + + isHoldSection = true; + } + + // 1.section is exist or empty section + if (_iniData.isSectionExists(Section) || Section == "") + { + // write key/value to head + if (isHoldSection) + { + output << newLine << "[" << Section << "]" << "\n"; + } + output << keyValueData; + // write others + std::string lineData; + while (std::getline(input, lineData)) + { + output << lineData << "\n"; + } + } + // 2.section is not exist + else + { + // write others + std::string lineData; + while (std::getline(input, lineData)) + { + output << lineData << "\n"; + } + // write key/value to end + if (isHoldSection) + { + output << newLine << "[" << Section << "]" << "\n"; + } + output << keyValueData; + } + + break; + } + else + { // found, replace it + + std::string lineData; + int input_line_number = 0; + + while (std::getline(input, lineData)) + { + ++input_line_number; + + // delete old comment if new comment is set + if (input_line_number == (line_number_mark - 1) && lineData.length() > 0 && lineData[0] == ';' && comment != "") + { + continue; + } + + if (input_line_number == line_number_mark) + { // replace to this line + output << keyValueData; + } + else + { + output << lineData << "\n"; + } + } + break; + } + + INI_DEBUG("error! inicpp lost process of modify function"); + return false; + + } while (false); + + // clear work + input.close(); + output.close(); + + std::remove(_configFileName.c_str()); + std::rename(tempFile.c_str(), _configFileName.c_str()); + + // reload + parse(); + + return true; + } + + bool modify(const std::string &Section, const std::string &Key, const int &Value, const std::string &comment = "") + { + std::string stringValue = std::to_string(Value); + return modify(Section, Key, stringValue, comment); + } + + bool modify(const std::string &Section, const std::string &Key, const double &Value, const std::string &comment = "") + { + std::string stringValue = std::to_string(Value); + return modify(Section, Key, stringValue, comment); + } + + bool modify(const std::string &Section, const std::string &Key, const std::wstring &Value, const std::string &comment = "") + { + std::wstring_convert> converter; + std::string stringValue = converter.to_bytes(Value); + + return modify(Section, Key, stringValue, comment); + } + + bool modifyComment(const std::string &Section, const std::string &Key, const std::string &comment) + { + return modify(Section, Key, (*this)[Section][Key], comment); + } + + bool isSectionExists(const std::string §ionName) + { + return _iniData.isSectionExists(sectionName); + } + + inline std::list getSectionsList() + { + return _iniData.getSectionsList(); + } + + private: + bool filterData(std::string &data) + { + if (data.length() == 0) + { + return false; + } + + if (data[0] == ';') + { + return false; + } + + if (data[0] == '#') + { + return false; + } + + return true; + } + + void trimEdges(std::string &data) + { + // remove left ' ' and '\t' + data.erase(data.begin(), std::find_if(data.begin(), data.end(), [](unsigned char c) + { return !std::isspace(c); })); + // remove right ' ' and '\t' + data.erase(std::find_if(data.rbegin(), data.rend(), [](unsigned char c) + { return !std::isspace(c); }) + .base(), + data.end()); + + INI_DEBUG( "trimEdges data:|" << data << "|"); + } + + private: + ini _iniData; + int _SumOfLines; + std::fstream _iniFile; + std::string _configFileName; + }; + +} + +#endif diff --git a/scripts/unittest.py b/scripts/unittest.py old mode 100644 new mode 100755 diff --git a/scripts/unittest.sh b/scripts/unittest.sh old mode 100644 new mode 100755 diff --git a/src/comframe.cpp b/src/comframe.cpp index 1ae8940..4d3a186 100755 --- a/src/comframe.cpp +++ b/src/comframe.cpp @@ -12,121 +12,120 @@ #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) + { + fprintf(stderr, "...error open tty failed: %s @%s:%d\n", strerror(errno), __FILE__, __LINE__); + exit(__LINE__); + } + init_tty(tty_id, baudRate); + printf("COM '%s' is ready!\n", path); + return tty_id; +} -//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__); -// } -//} +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) + { + 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: + 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) + { + 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); @@ -280,7 +279,7 @@ ComFrame* ComFrame_ReceiveEx(int tty_id, void* buffer, const size_t buffer_size, #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); + fprintf(stderr, "data size is too large (%zu)\n", pred_size); find_head = false; pred_size = COM_FRAME_RECEIVE_PRED_SIZE; continue; diff --git a/src/event.cpp b/src/event.cpp index 49fc4b6..5935e99 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -6,7 +6,6 @@ #include "dataqueue.hpp" std::unordered_map>> _events; -bool _event_thread_running = false; DataQueue> _event_queue; void sys_event(const char *event_name, void *args) { @@ -35,9 +34,12 @@ int sys_event_unregister(const char *event_name, event_callback callback) { } void* sys_event_thread(void*) { - _event_thread_running = true; - while (_event_thread_running) { - auto [event_name, callback, args, user_data] = _event_queue.Pop(); + while (true) { + const char* event_name; + event_callback callback; + void* args; + void* user_data; + std::tie(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 index c643d5f..0f9b38f 100644 --- a/src/extern_interface.cpp +++ b/src/extern_interface.cpp @@ -1,3 +1,37 @@ +#include #include "extern_interface.h" +#include "event.h" +#include "inicpp.hpp" +static et_callback_t et_callback; +static pthread_t event_thread; +static pthread_t upperhost_thread; +static pthread_t telemetry_thread; +int extern_interface_init(const char* config_path, et_callback_t cb) { + inicpp::IniManager manager(config_path); + et_callback = cb; + + pthread_create(&event_thread, NULL, sys_event_thread, NULL); +} + +void extern_interface_send(uint32_t type, size_t len, union PacketData data) { + switch (type) { + case ET_TYPE_COMMAND: + sys_event("send_command", data.pd_pointer); + break; + case ET_TYPE_AUDIO: + sys_event("send_audio", data.pd_pointer); + break; + default: + break; + } +} + +void extern_interface_handle(uint32_t type, size_t len, union PacketData data) { + et_callback(type, len, data); +} + +void get_telemetry_request_data(TelemetryRequestData* data) { + extern_interface_handle(ET_TYPE_TELEMETRY_REQUEST, sizeof(TelemetryRequestData), { .pd_pointer = data }); +} diff --git a/src/host_com.cpp b/src/host_com.cpp index 35bc0d9..da1b663 100644 --- a/src/host_com.cpp +++ b/src/host_com.cpp @@ -3,11 +3,11 @@ #include #include #include "telemetry.h" -#include "dataqueue.h" -#include "defines.h" +#include "dataqueue.hpp" #include "command.h" -extern bool g_bKeepSysRuning; +#define PrintFilePos() + void play_text(char* pTextStr, int iRepeat_times); // int32_t g_iExternTimeDifference = 0; diff --git a/tests/c_testcase.cpp b/tests/c_testcase.cpp index 5535f69..329c8a4 100644 --- a/tests/c_testcase.cpp +++ b/tests/c_testcase.cpp @@ -32,7 +32,7 @@ static interactive_func interactive = NULL; static context_func setup = NULL; static context_func teardown = NULL; -static atomic_bool testcase_running = false; +static atomic_bool testcase_running; static jmp_buf testcase_env; static int testcase_exit_code = 0; diff --git a/tests/test_queue.cpp b/tests/test_queue.cpp index ca32165..380cb1b 100644 --- a/tests/test_queue.cpp +++ b/tests/test_queue.cpp @@ -7,3 +7,5 @@ TEST_CASE(init) { assert_eq(q.Size(), 0); END_TEST; } + + diff --git a/toolchains/armv7l_linux_setup.cmake b/toolchains/armv7l_linux_setup.cmake new file mode 100644 index 0000000..aa2603a --- /dev/null +++ b/toolchains/armv7l_linux_setup.cmake @@ -0,0 +1,5 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER arm-none-linux-gnueabihf-gcc) +set(CMAKE_CXX_COMPILER arm-none-linux-gnueabihf-g++) diff --git a/toolchains/armv7l_openeuler_setup.cmake b/toolchains/armv7l_openeuler_setup.cmake new file mode 100644 index 0000000..5cc9108 --- /dev/null +++ b/toolchains/armv7l_openeuler_setup.cmake @@ -0,0 +1,5 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER arm-ztkp_openeuler-linux-gnueabi-gcc) +set(CMAKE_CXX_COMPILER arm-ztkp_openeuler-linux-gnueabi-g++) diff --git a/toolchains/libgo-crosscompile.cmake b/toolchains/libgo-crosscompile.cmake new file mode 100644 index 0000000..edddb81 --- /dev/null +++ b/toolchains/libgo-crosscompile.cmake @@ -0,0 +1,203 @@ +cmake_minimum_required(VERSION 2.8) + +################################################################################### +project(libgo) + +enable_language(C ASM) + +if (CMAKE_BUILD_TYPE) +else() + set(CMAKE_BUILD_TYPE RELEASE) + #set(CMAKE_BUILD_TYPE DEBUG) +endif() + +message("------------ Options -------------") +message(" CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +message(" CMAKE_COMMAND: ${CMAKE_COMMAND}") + +option(ENABLE_DEBUGGER "enable debugger" OFF) +if (ENABLE_DEBUGGER) + set(ENABLE_DEBUGGER 1) + message (" enable_debugger: yes") +else() + set(ENABLE_DEBUGGER 0) + message (" enable_debugger: no") +endif() + +option(DISABLE_HOOK "disable hook" OFF) +if (DISABLE_HOOK) + set(ENABLE_HOOK 0) + message (" enable_hook: no") +else() + set(ENABLE_HOOK 1) + message (" enable_hook: yes") +endif() + +if (BUILD_DYNAMIC) + message (" build dynamic lib: yes") +else() + message (" build dynamic lib: no") +endif() + +message("-------------- Env ---------------") +message(" CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") +message(" CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") +message("----------------------------------") + +configure_file(${PROJECT_SOURCE_DIR}/libgo/common/cmake_config.h.in ${PROJECT_SOURCE_DIR}/libgo/common/cmake_config.h) +message("----------------------------------") + +if (UNIX) + set(CMAKE_CXX_FLAGS "-std=c++17 -fPIC -Wall ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "-g") + set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -DNDEBUG") + + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS S) + if(CMAKE_CROSSCOMPILING) + message(STATUS "> CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") + set(ASM_DIR "${PROJECT_SOURCE_DIR}/third_party/boost.context/libs/context/src/asm/") + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + set(jump_asm_file "${ASM_DIR}jump_arm_aapcs_elf_gas.S") + set(make_asm_file "${ASM_DIR}make_arm_aapcs_elf_gas.S") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(jump_asm_file "${ASM_DIR}jump_arm64_aapcs_elf_gas.S") + set(make_asm_file "${ASM_DIR}make_arm64_aapcs_elf_gas.S") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(jump_asm_file "${ASM_DIR}jump_x86_64_sysv_elf_gas.S") + set(make_asm_file "${ASM_DIR}make_x86_64_sysv_elf_gas.S") + endif() + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") # macOS + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + set(jump_asm_file "${ASM_DIR}jump_arm64_aapcs_macho_gas.S") + set(make_asm_file "${ASM_DIR}make_arm64_aapcs_macho_gas.S") + endif() + elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") + set(jump_asm_file "${ASM_DIR}jump_i386_ms_pe_gas.asm") + set(make_asm_file "${ASM_DIR}make_i386_ms_pe_gas.asm") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(jump_asm_file "${ASM_DIR}jump_x86_64_ms_pe_gas.asm") + set(make_asm_file "${ASM_DIR}make_x86_64_ms_pe_gas.asm") + endif() + endif() + if (NOT jump_asm_file OR NOT make_asm_file) + message(FATAL_ERROR "--> select asm source file failed") + return() + endif() + file(COPY ${jump_asm_file} DESTINATION ${PROJECT_SOURCE_DIR}/libgo/context) + file(COPY ${make_asm_file} DESTINATION ${PROJECT_SOURCE_DIR}/libgo/context) + else() + message("--> select asm source file, please wait about 5 seconds ...") + execute_process(COMMAND "${PROJECT_SOURCE_DIR}/third_party/select_asm.sh" "${PROJECT_SOURCE_DIR}" "jump" OUTPUT_VARIABLE jump_asm_file) + execute_process(COMMAND "${PROJECT_SOURCE_DIR}/third_party/select_asm.sh" "${PROJECT_SOURCE_DIR}" "make" OUTPUT_VARIABLE make_asm_file) + endif() + + # 输出选择的汇编文件路径 + message(STATUS "Jump ASM File: ${jump_asm_file}") + message(STATUS "Make ASM File: ${make_asm_file}") +elseif (WIN32) + # windows platform + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /EHsc") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /EHsc") + + #set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS asm) + #file(COPY ${PROJECT_SOURCE_DIR}/third_party/boost.context/libs/context/src/asm/make_x86_64_ms_pe_masm.asm DESTINATION ${PROJECT_SOURCE_DIR}/libgo/context) + #file(COPY ${PROJECT_SOURCE_DIR}/third_party/boost.context/libs/context/src/asm/jump_x86_64_ms_pe_masm.asm DESTINATION ${PROJECT_SOURCE_DIR}/libgo/context) +endif() + +message("------------ Cxx flags -------------") +message(" CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}: ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") +message("------------------------------------") + +include_directories(${PROJECT_SOURCE_DIR}) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/common CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/context CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/task CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/scheduler CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/sync CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/timer CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/cls CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/defer CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/pool CO_SRC_LIST) +aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/debug CO_SRC_LIST) + +if (NOT DISABLE_HOOK) + if (UNIX) + aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/netio/unix CO_SRC_LIST) + elseif (WIN32) + include_directories(${PROJECT_SOURCE_DIR}/libgo/netio/windows) + aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/netio/windows CO_SRC_LIST) + aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/context/fiber CO_SRC_LIST) + list(APPEND CO_SRC_LIST ${PROJECT_SOURCE_DIR}/libgo/netio/windows/xhook/xhook.cpp) + endif() +else() + aux_source_directory(${PROJECT_SOURCE_DIR}/libgo/netio/disable_hook CO_SRC_LIST) +endif() + +set(TARGET "libgo") +set(STATIC_T "libgo_static") +set(STATIC_HOOK "static_hook") + +list(APPEND CO_SRC_LIST ${jump_asm_file}) +list(APPEND CO_SRC_LIST ${make_asm_file}) +add_library("${STATIC_T}" STATIC ${CO_SRC_LIST}) +set_target_properties("${STATIC_T}" PROPERTIES OUTPUT_NAME "${TARGET}") + +if (UNIX) + install(TARGETS ${STATIC_T} LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "lib") + install(DIRECTORY ${PROJECT_SOURCE_DIR}/libgo/ DESTINATION "include/libgo" FILES_MATCHING PATTERN "*.h") + #PATTERN "windows" EXCLUDE + + add_library("${STATIC_HOOK}" STATIC "${PROJECT_SOURCE_DIR}/libgo/netio/unix/static_hook/static_hook.cpp") + + if (BUILD_DYNAMIC) + set(SHARED_T "libgo_dynamic") + add_library("${SHARED_T}" SHARED ${CO_SRC_LIST}) + set_target_properties("${SHARED_T}" PROPERTIES OUTPUT_NAME "${TARGET}") + target_link_libraries("${SHARED_T}" ${LINK_LIBS} -ldl) + install(TARGETS ${SHARED_T} LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "lib") + endif() + + add_custom_target(debug + COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=DEBUG ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Switch CMAKE_BUILD_TYPE to Debug" + ) + + add_custom_target(release + COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=RELEASE ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Switch CMAKE_BUILD_TYPE to Release" + ) + + set(PROFILE_FLAGS "-pg ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") + + #message("PROFILE_FLAGS: ${PROFILE_FLAGS}") + add_custom_target(profile + COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=PROFILE -DCMAKE_CXX_FLAGS_PROFILE=\\'${PROFILE_FLAGS}\\' ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Switch CMAKE_BUILD_TYPE to PROFILE" + ) + + add_custom_target(uninstall + COMMAND rm ${CMAKE_INSTALL_PREFIX}/lib/liblibgo.a ${CMAKE_INSTALL_PREFIX}/lib/liblibgo.so ${CMAKE_INSTALL_PREFIX}/lib/liblibgo_main.a -f + COMMAND rm ${CMAKE_INSTALL_PREFIX}/include/libgo -rf + ) + +elseif (WIN32) + set_target_properties("${STATIC_T}" PROPERTIES COMPILE_FLAGS "/wd4819 /wd4267") +endif() + +if (WIN32) + if (BOOST_ROOT) + add_subdirectory(${PROJECT_SOURCE_DIR}/test/gtest_unit) + add_subdirectory(${PROJECT_SOURCE_DIR}/tutorial) + endif() +endif()