From 566d30b5f8426928ae15ad1ca1946f723d4672f8 Mon Sep 17 00:00:00 2001 From: Sherlock <1297399478@qq.com> Date: Thu, 12 Jun 2025 15:28:37 +0800 Subject: [PATCH] =?UTF-8?q?CP=E6=8E=A7=E5=88=B6V1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + CMakeLists.txt | 28 ++ CPCtrlConfig.ini | 10 + ComCtrl/ComCtrl.cpp | 63 ++++ ComCtrl/ComCtrl.h | 21 ++ Main/GlobalDefs.h | 12 + Main/Send.cpp | 92 +++++ Main/Send.h | 26 ++ Noncanonical/Noncanonical.cpp | 309 +++++++++++++++++ Noncanonical/Noncanonical.h | 38 ++ Serial/SerialPort.cpp | 250 ++++++++++++++ Serial/SerialPort.h | 63 ++++ Udp/ConfigParser.cpp | 632 ++++++++++++++++++++++++++++++++++ Udp/ConfigParser.h | 268 ++++++++++++++ main.cpp | 83 +++++ 15 files changed, 1899 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CPCtrlConfig.ini create mode 100644 ComCtrl/ComCtrl.cpp create mode 100644 ComCtrl/ComCtrl.h create mode 100644 Main/GlobalDefs.h create mode 100644 Main/Send.cpp create mode 100644 Main/Send.h create mode 100644 Noncanonical/Noncanonical.cpp create mode 100644 Noncanonical/Noncanonical.h create mode 100644 Serial/SerialPort.cpp create mode 100644 Serial/SerialPort.h create mode 100644 Udp/ConfigParser.cpp create mode 100644 Udp/ConfigParser.h create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f81fcb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +cmake-build-debug/ +cmake-build-release/ +cmake-build-release-jr/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..98a960e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.28) +project(CpCtrl) + +set(CMAKE_CXX_STANDARD 11) +include_directories(Main) +aux_source_directory(Main MAIN_LIST) +include_directories(ComCtrl) +aux_source_directory(ComCtrl COM_LIST) +include_directories(Udp) +aux_source_directory(Udp UDP_LIST) +include_directories(Serial) +aux_source_directory(Serial SERIAL_LIST) +include_directories(Noncanonical) +aux_source_directory(Noncanonical NONCAL_LIST) + +add_executable(CpCtrl main.cpp ${MAIN_LIST} ${COM_LIST} ${UDP_LIST} ${SERIAL_LIST} ${NONCAL_LIST}) + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_link_libraries(CpCtrl PRIVATE ws2_32 kernel32) +else () + target_link_libraries(CpCtrl PRIVATE pthread) +endif () + +#Windows下拷贝配置文件到执行目录 +set(INI_FILES CPCtrlConfig.ini) +foreach (file ${INI_FILES}) + configure_file("${file}" "${CMAKE_CURRENT_BINARY_DIR}/${file}" COPYONLY) +endforeach () \ No newline at end of file diff --git a/CPCtrlConfig.ini b/CPCtrlConfig.ini new file mode 100644 index 0000000..15be136 --- /dev/null +++ b/CPCtrlConfig.ini @@ -0,0 +1,10 @@ +# +mode=udp +#udp +ip=127.0.0.1 +sendPort=9001 +recvPort=9002 +#serial +device=/dev/pts/3 +baudRate=115200 +hh=false \ No newline at end of file diff --git a/ComCtrl/ComCtrl.cpp b/ComCtrl/ComCtrl.cpp new file mode 100644 index 0000000..d3cb787 --- /dev/null +++ b/ComCtrl/ComCtrl.cpp @@ -0,0 +1,63 @@ +#include "ComCtrl.h" + +void SendCmd(const vector &args, const string &type) { + string tmpCmd = type + args[1] + "," + args[2] + "\r\n"; + myDateTime tmp; + string log = tmp.toDateTime() + " Send: " + tmpCmd; + cout << log; + historyList.push_back(log); + string tmpCmdList = args[0] + " " + args[1] + " " + args[2]; + if (cmdList.empty()) { + cmdList.push_back(tmpCmdList); + } + else { + if (cmdList.back() != tmpCmdList) { + cmdList.push_back(tmpCmdList); + } + } + if (Sender.sendto(tmpCmd) < 0) { + cerr << "send error" << endl; + } +} + +void setantCommand(const vector &args) { + if (args.size() != 3) { + cout << "Usage: setant " << endl; + return; + } + SendCmd(args, "AT+SetAnt="); +} + +void setephCommand(const vector &args) { + if (args.size() != 3) { + cout << "Usage: seteph " << endl; + return; + } + SendCmd(args, "AT+SetEph="); +} + +// 查询键值 +void queryCommand(const vector &args) { + if (args.size() != 3) { + cout << "Usage: query " << endl; + return; + } + SendCmd(args, "AT+Query="); +} + +void listCommand() { + for (const auto &i: historyList) { + cout << i << flush; + } + string tmpCmdList = "list"; + if (cmdList.empty()) { + cmdList.push_back(tmpCmdList); + } + else { + if (cmdList.back() != tmpCmdList) { + cmdList.push_back(tmpCmdList); + } + } +} + + diff --git a/ComCtrl/ComCtrl.h b/ComCtrl/ComCtrl.h new file mode 100644 index 0000000..4a9441f --- /dev/null +++ b/ComCtrl/ComCtrl.h @@ -0,0 +1,21 @@ +#ifndef CPCTRL_COMCTRL_H +#define CPCTRL_COMCTRL_H + +#include "GlobalDefs.h" +#include "Noncanonical.h" +#include + +using namespace std; + +// 设置 +void setantCommand(const vector& args); +void setephCommand(const vector& args); + +// 查询 +void queryCommand(const vector& args); + +//历史 +void listCommand(); + + +#endif //CPCTRL_COMCTRL_H diff --git a/Main/GlobalDefs.h b/Main/GlobalDefs.h new file mode 100644 index 0000000..aa58c29 --- /dev/null +++ b/Main/GlobalDefs.h @@ -0,0 +1,12 @@ +#ifndef CPCTRL_GLOBALDEFS_H +#define CPCTRL_GLOBALDEFS_H +#include +#include +#include +#include "Send.h" + +// 全局键值对存储 +extern std::vector historyList; +extern Send Sender; + +#endif //CPCTRL_GLOBALDEFS_H diff --git a/Main/Send.cpp b/Main/Send.cpp new file mode 100644 index 0000000..9657df9 --- /dev/null +++ b/Main/Send.cpp @@ -0,0 +1,92 @@ +#include +#include "Send.h" +#include "GlobalDefs.h" + + +using namespace std; +Send::Send() { + try { + running_ = true; + std::unordered_map config; + configFromIni("./CPCtrlConfig.ini", config); + if (config["mode"] == "udp") { + isUdp = true; + UdpReceiver.reset(new UDPReceiver(stoi(config["recvPort"]))); + UdpReceiver->start_receiving(([](const std::vector &data, sockaddr_in addr, + socklen_t len) { + myDateTime tmp; + string log = tmp.toDateTime() + " Recv: " + string(data.begin(), data.end()); + cout << log; + historyList.push_back(log); + })); + UdpSender.reset(new UDPSender(UdpReceiver->sockfd_)); + UdpSender->set_destination(config["ip"], stoi(config["sendPort"])); + cout << "udp link [" + config["recvPort"] + "] sendto \"" + config["ip"] + ":" + config["sendPort"]+"\"" << endl; + } + else { + isUdp = false; + if (config["hh"] == "true") { + isHH = true; + } + else { + isHH = false; + } + Serial.reset(new SerialPort(config["device"], stoi(config["baudRate"]))); + Serial->start_receiving(([](const std::vector &data) { + myDateTime tmp; + string log = tmp.toDateTime() + " Recv: " + string(data.begin(), data.end()); + cout << log; + historyList.push_back(log); + })); + cout << "serial link [" + config["device"] + "] baudRate:" + config["baudRate"] + " HH:" + config["hh"]<send((uint8_t *) data.data(), data.size()); + } + else { + if (isHH) { + char tmp[6] = "\xeb\x90\x07\x00"; + uint16_t len = htons(data.size()); + memcpy(tmp + 4, &len, 2); + string str = string(tmp,tmp+6) + data; + return Serial->write(str); + } + else { + return Serial->write(data); + } + } +} + +size_t Send::sendto(const vector &data) { + if (isUdp) { + return UdpSender->send((uint8_t *) data.data(), data.size()); + } + else { + if (isHH) { + vector tmpStr(data); + char tmp[6] = "\xeb\x90\x07\x00"; + uint16_t len = htons(data.size()); + memcpy(tmp + 4, &len, 2); + vector str = vector(tmp,tmp+6); + for (auto i: data) { + str.push_back(i); + } + return Serial->write(str); + } + else { + return Serial->write(data); + } + } +} + +bool Send::isRunning() const { + return running_; +} diff --git a/Main/Send.h b/Main/Send.h new file mode 100644 index 0000000..eba9af0 --- /dev/null +++ b/Main/Send.h @@ -0,0 +1,26 @@ +#ifndef CPCTRL_SEND_H +#define CPCTRL_SEND_H + +#include "ConfigParser.h" +#include "SerialPort.h" + +class Send { +public: + Send(); + + size_t sendto(const std::string &data); + size_t sendto(const std::vector &data); + + bool isRunning() const; + +private: + std::unique_ptr UdpReceiver{}; + std::unique_ptr UdpSender{}; + std::unique_ptr Serial{}; + bool isUdp{}; + bool isHH{}; + bool running_{}; +}; + + +#endif //CPCTRL_SEND_H diff --git a/Noncanonical/Noncanonical.cpp b/Noncanonical/Noncanonical.cpp new file mode 100644 index 0000000..545e59f --- /dev/null +++ b/Noncanonical/Noncanonical.cpp @@ -0,0 +1,309 @@ +#include "Noncanonical.h" + +using namespace std; + +// 全局终端设置 +terminal_mode_t original_termios; +std::vector cmdList{}; + + +// 支持的命令列表 +const vector commands = {"setant", "seteph", "query","list", "exit", "help"}; + +void restore_terminal() { +#if defined(_WIN32) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), original_termios); +#else + tcsetattr(STDIN_FILENO, TCSANOW, &original_termios); +#endif +} + +void set_noncanonical_mode() { +#if defined(_WIN32) + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hStdin, &original_termios); + + DWORD new_mode = original_termios; + new_mode &= ~ENABLE_ECHO_INPUT; // 禁用回显 + new_mode &= ~ENABLE_LINE_INPUT; // 禁用行缓冲 + new_mode &= ~ENABLE_PROCESSED_INPUT; // 禁用Ctrl+C处理 + + SetConsoleMode(hStdin, new_mode); +#else + struct termios new_termios; + tcgetattr(STDIN_FILENO, &original_termios); + new_termios = original_termios; + + new_termios.c_lflag &= ~(ICANON | ECHO | ISIG); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + + tcsetattr(STDIN_FILENO, TCSANOW, &new_termios); +#endif + + atexit(restore_terminal); +} + +string toLower(const string &str) { + string result; + for (char c : str) { + result += tolower(c); + } + return result; +} +string tabCompletion(const string ¤tLine, size_t cursorPos) { + // 分割当前行 + vector tokens; + string token; + for (char c: currentLine) { + if (isspace(c)) { + if (!token.empty()) { + tokens.push_back(token); + token.clear(); + } + } + else { + token += c; + } + } + if (!token.empty()) tokens.push_back(token); + + // 如果没有token或当前token为空 + if (tokens.empty() || token.empty()) return currentLine; + + bool isFirstToken = (tokens.size() == 1); + string lastToken = tokens.back(); + + // 候选列表 + vector candidates; + + // 第一个token:匹配命令 + if (isFirstToken) { + for (const string &cmd: commands) { + if (cmd.substr(0, lastToken.size()) == lastToken) { + candidates.push_back(cmd); + } + } + } +// // 第二个token:匹配键 +// else if (tokens.size() == 2) { +// for (const auto &kv: kvStore) { +// if (kv.first.substr(0, lastToken.size()) == lastToken) { +// candidates.push_back(kv.first); +// } +// } +// } + + // 没有候选结果 + if (candidates.empty()) { +// cout << "\n> " << currentLine << std::flush; + return currentLine; + } + + // 唯一候选结果 - 直接补全 + if (candidates.size() == 1) { + // 计算要删除的字符数 + size_t charsToErase = lastToken.size(); + + // 创建新行 + string newLine = currentLine.substr(0, currentLine.size() - charsToErase); + newLine += candidates[0]; + if (isFirstToken) newLine += " "; + + return newLine; + } + + // 多个候选结果 - 打印选项 + cout << "\nPossible completions:\n"; + for (const auto &cand: candidates) { + cout << " " << cand << "\n"; + } + cout << "> " << currentLine << std::flush; + + + return currentLine; +} + +string readLine(const string &prompt) { + cout << prompt << flush; + string line; + static int historyIndex = -1; // 当前历史命令索引 + static string tempLine = ""; // 临时保存当前编辑行 + +#if defined(_WIN32) + while (true) { + int ch = _getch(); // Windows专用字符读取 + + // 处理特殊键 + if (ch == 0x00 || ch == 0xE0) { + int ext = _getch(); // 获取扩展按键 + // 上方向键 + if (ext == 72) { + if (!cmdList.empty()) { + // 保存当前编辑行 + if (historyIndex == -1) tempLine = line; + + // 更新历史索引 + if (historyIndex < cmdList.size() - 1) + historyIndex++; + else if (historyIndex == -1) + historyIndex = 0; + + //清除 + cout << "\r" << prompt << string(line.size(), ' ') << flush; + + // 更新当前行 + line = cmdList[cmdList.size() - 1 - historyIndex]; + + //重绘 + cout << "\r" << prompt << line << flush; + } + continue; + } + // 下方向键 + else if (ext == 80) { + if (historyIndex != -1) { + //清除 + cout << "\r" << prompt << string(line.size(), ' ') << flush; + if (historyIndex > 0) { + historyIndex--; + line = cmdList[cmdList.size() - 1 - historyIndex]; + } + else { + historyIndex = -1; + line = tempLine; + } + + //重绘 + cout << "\r" << prompt << line << flush; + } + continue; + } + } + + switch (ch) { + case '\r': // Enter键 + cout << endl; + return line; + + case '\t': // Tab键 + line = tabCompletion(line, line.size()); + cout << "\r" << prompt << string(line.size(), ' ') << flush; + cout << "\r" << prompt << line << flush; + break; + + case 8: // Backspace键 + if (!line.empty()) { + line.pop_back(); + cout << "\b \b" << flush; + } + break; + + case 3: // Ctrl+C + cout << endl; + exit(0); + break; + + default: // 可打印字符 + if (isprint(ch)) { + line += ch; + cout << static_cast(ch) << flush; + } + } + } +#else + char ch; + + while (read(STDIN_FILENO, &ch, 1) == 1) { + // 处理方向键序列 (ESC [ A 或 ESC [ B) + if (ch == 27) { // ESC + char seq[2]; + if (read(STDIN_FILENO, seq, 2) == 2) { + // 上方向键 + if (seq[0] == '[' && seq[1] == 'A') { + if (!cmdList.empty()) { + // 保存当前编辑行 + if (historyIndex == -1) tempLine = line; + + // 更新历史索引 + if (historyIndex < cmdList.size() - 1) + historyIndex++; + else if (historyIndex == -1) + historyIndex = 0; + + for (size_t i = 0; i < line.size(); i++) cout << "\b \b"; + + // 更新当前行 + line = cmdList[cmdList.size() - 1 - historyIndex]; + + // 清除并重绘 + + cout << line << flush; + } + continue; + } + // 下方向键 + else if (seq[0] == '[' && seq[1] == 'B') { + if (historyIndex != -1) { + for (size_t i = 0; i < line.size(); i++) cout << "\b \b"; + if (historyIndex > 0) { + historyIndex--; + line = cmdList[cmdList .size() - 1 - historyIndex]; + } else { + historyIndex = -1; + line = tempLine; + } + + // 清除并重绘 + cout << line << flush; + } + continue; + } + } + } + // Enter键 - 完成输入 + if (ch == '\n') { + cout << endl; + return line; + } + + // Tab键 - 触发补全 + if (ch == '\t') { + string newLine = tabCompletion(line, line.size()); + if (newLine != line) { + // 清除当前行 + for (size_t i = 0; i < line.size(); i++) { + cout << "\b \b"; + } + + line = newLine; + cout << line << flush; + } + continue; + } + + // Backspace键 - 删除字符(支持不同终端的两种编码) + if (ch == 127 || ch == 8) { + if (!line.empty()) { + line.pop_back(); + // 清除字符:\b 移动光标,空格覆盖字符,再\b移动光标 + cout << "\b \b" << flush; + } + continue; + } + + // Ctrl+C - 退出程序 + if (ch == 3) { + cout << endl; + exit(0); + } + + // 其他可打印字符 + if (isprint(ch)) { + line += ch; + cout << ch << flush; + } + } +#endif + return line; +} diff --git a/Noncanonical/Noncanonical.h b/Noncanonical/Noncanonical.h new file mode 100644 index 0000000..cb2c90b --- /dev/null +++ b/Noncanonical/Noncanonical.h @@ -0,0 +1,38 @@ +#ifndef CPCTRL_NONCANONICAL_H +#define CPCTRL_NONCANONICAL_H + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#endif + +//#include "GlobalDefs.h" +extern std::vector cmdList; + +// 平台相关类型定义 +#if defined(_WIN32) +typedef DWORD terminal_mode_t; +#else +typedef struct termios terminal_mode_t; +#endif + +// 全局保存终端原始设置 +extern terminal_mode_t original_termios; + +void restore_terminal(); +void set_noncanonical_mode(); +std::string toLower(const std::string& str); +std::string tabCompletion(const std::string& currentLine, size_t cursorPos); +std::string readLine(const std::string& prompt); + +#endif //CPCTRL_NONCANONICAL_H \ No newline at end of file diff --git a/Serial/SerialPort.cpp b/Serial/SerialPort.cpp new file mode 100644 index 0000000..122d82a --- /dev/null +++ b/Serial/SerialPort.cpp @@ -0,0 +1,250 @@ +#include +#include "SerialPort.h" + +#include +#include + + +SerialPort::SerialPort(const std::string& device, int baudrate) : running_(false) { +#ifdef _WIN32 + fd_ = CreateFile(device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd_ == INVALID_HANDLE_VALUE) { + throw std::system_error(GetLastError(), std::system_category(), "Failed to open serial port"); + } + + DCB dcbSerialParams = {0}; + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + if (!GetCommState(fd_, &dcbSerialParams)) { + CloseHandle(fd_); + throw std::system_error(GetLastError(), std::system_category(), "GetCommState failed"); + } + + dcbSerialParams.BaudRate = baudrate; + dcbSerialParams.ByteSize = 8; + dcbSerialParams.Parity = NOPARITY; + dcbSerialParams.StopBits = ONESTOPBIT; + dcbSerialParams.fOutxCtsFlow = FALSE; + dcbSerialParams.fOutxDsrFlow = FALSE; + dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; + dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE; + + if (!SetCommState(fd_, &dcbSerialParams)) { + CloseHandle(fd_); + throw std::system_error(GetLastError(), std::system_category(), "SetCommState failed"); + } + + COMMTIMEOUTS timeouts = {0}; + timeouts.ReadIntervalTimeout = 50; + timeouts.ReadTotalTimeoutConstant = 50; + timeouts.ReadTotalTimeoutMultiplier = 10; + timeouts.WriteTotalTimeoutConstant = 50; + timeouts.WriteTotalTimeoutMultiplier = 10; + if (!SetCommTimeouts(fd_, &timeouts)) { + CloseHandle(fd_); + throw std::system_error(GetLastError(), std::system_category(), "SetCommTimeouts failed"); + } +#else + fd_ = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + if (fd_ < 0) { + throw std::system_error(errno, std::generic_category(), "Failed to open serial port"); + } + configure(baudrate, fd_); +#endif +} + +SerialPort::~SerialPort() { + stop_receiving(); + close(); +} + +void SerialPort::configure(int baudrate, int fd) { +#ifdef _WIN32 + // Windows配置在构造函数中已完成 +#else + termios tty{}; + if (tcgetattr(fd, &tty) != 0) { + throw std::system_error(errno, std::generic_category(), "tcgetattr failed"); + } + + speed_t speed = getBaudRate(baudrate); + cfsetispeed(&tty, speed); + cfsetospeed(&tty, speed); + + tty.c_cflag &= ~PARENB; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CSIZE; + tty.c_cflag |= CS8; + tty.c_cflag &= ~CRTSCTS; + tty.c_cflag |= CREAD | CLOCAL; + + tty.c_lflag &= ~(ICANON | ECHO | ECHOE | IEXTEN | ISIG); + tty.c_iflag &= ~(ICRNL | INLCR | IGNCR); + tty.c_iflag |= IGNBRK; + + tty.c_oflag &= ~OPOST; + + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + if (tcsetattr(fd, TCSANOW, &tty) != 0) { + throw std::system_error(errno, std::generic_category(), "tcsetattr failed"); + } + + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +#endif +} + +ssize_t SerialPort::write(const std::vector& data) { +#ifdef _WIN32 + DWORD bytesWritten; + if (!WriteFile(fd_, data.data(), static_cast(data.size()), &bytesWritten, NULL)) { + throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed"); + } + return static_cast(bytesWritten); +#else + ssize_t result = ::write(fd_, data.data(), data.size()); + if (result < 0) { + throw std::system_error(errno, std::generic_category(), "write failed"); + } + return result; +#endif +} + +std::vector SerialPort::read() { +#ifdef _WIN32 + std::vector buffer(256); + DWORD bytesRead; + if (!ReadFile(fd_, buffer.data(), static_cast(buffer.size()), &bytesRead, NULL)) { + throw std::system_error(GetLastError(), std::system_category(), "ReadFile failed"); + } + buffer.resize(bytesRead); + return buffer; +#else + std::vector buffer(256); + ssize_t n = ::read(fd_, buffer.data(), buffer.size()); + if (n > 0) { + buffer.resize(n); + return buffer; + } + return {}; +#endif +} + +void SerialPort::close() { +#ifdef _WIN32 + if (fd_ != INVALID_HANDLE_VALUE) { + CloseHandle(fd_); + fd_ = INVALID_HANDLE_VALUE; + } +#else + if (fd_ >= 0) { + ::close(fd_); + fd_ = -1; + } +#endif +} + +speed_t SerialPort::getBaudRate(int baudrate) { +#ifdef _WIN32 + // Windows直接使用数值波特率 + return baudrate; +#else + switch (baudrate) { + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 921600: return B921600; + case 1000000: return B1000000; + case 2000000: return B2000000; + case 4000000: return B4000000; + default: + throw std::invalid_argument("Unsupported baudrate"); + } +#endif +} + +void SerialPort::start_receiving(const ReceiveCallback& callback) { + running_ = true; + receiver_thread_ = std::thread([this, callback]() { + while (running_.load(std::memory_order_relaxed)) { + try { + auto data = read(); + if (!data.empty()) { + callback(data); + } + } catch (const std::system_error& e) { + if (!running_) break; // 正常退出 + else throw; // 重新抛出其他错误 + } + } + }); +} + +void SerialPort::stop_receiving() { + running_.store(false); +#ifdef _WIN32 + // Windows通过关闭句柄唤醒阻塞的ReadFile + if (fd_ != INVALID_HANDLE_VALUE) { + CancelIoEx(fd_, NULL); + } +#else + // Linux通过关闭文件描述符唤醒阻塞的read + if (fd_ >= 0) { + ::close(fd_); + fd_ = -1; + } +#endif + if (receiver_thread_.joinable()) { + receiver_thread_.detach(); + } +} + +ssize_t SerialPort::write(unsigned char* str, int size) { +#ifdef _WIN32 + DWORD bytesWritten; + if (!WriteFile(fd_, str, static_cast(size), &bytesWritten, NULL)) { + throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed"); + } + return static_cast(bytesWritten); +#else + ssize_t result = ::write(fd_, str, size); + if (result < 0) { + throw std::system_error(errno, std::generic_category(), "write failed"); + } + return result; +#endif +} + +ssize_t SerialPort::write(const std::string& data) { +#ifdef _WIN32 + DWORD bytesWritten; + if (!WriteFile(fd_, data.data(), static_cast(data.size()), &bytesWritten, NULL)) { + throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed"); + } + return static_cast(bytesWritten); +#else + ssize_t result = ::write(fd_, data.data(), data.size()); + if (result < 0) { + throw std::system_error(errno, std::generic_category(), "write failed"); + } + return result; +#endif +} \ No newline at end of file diff --git a/Serial/SerialPort.h b/Serial/SerialPort.h new file mode 100644 index 0000000..16766da --- /dev/null +++ b/Serial/SerialPort.h @@ -0,0 +1,63 @@ +#ifndef ATCMD_SERIALPORT_H +#define ATCMD_SERIALPORT_H +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +using speed_t = unsigned int; +#else +#include +#include +#include +#endif + +class SerialPort { +public: + // 处理接收数据函数模板 + using ReceiveCallback = std::function &)>; + + // 打开串口设备 + SerialPort(const std::string &device, int baudrate); + + ~SerialPort(); + + // 配置串口参数 + void configure(int baudrate, int fd); + + // 发送数据 + ssize_t write(const std::vector& data); + ssize_t write(const std::string & data); + ssize_t write(unsigned char *str,int size); + + // 开始接收线程 + void start_receiving(const ReceiveCallback &callback); + + // 停止接收线程 + void stop_receiving(); + + void close(); + +private: +#ifdef _WIN32 + HANDLE fd_ = INVALID_HANDLE_VALUE; +#else + int fd_ = -1; +#endif + std::atomic running_; + std::thread receiver_thread_; + + // 接收数据 + std::vector read(); + + //获取波特率 + speed_t getBaudRate(int baudrate); +}; + + + +#endif //ATCMD_SERIALPORT_H diff --git a/Udp/ConfigParser.cpp b/Udp/ConfigParser.cpp new file mode 100644 index 0000000..75b0e2b --- /dev/null +++ b/Udp/ConfigParser.cpp @@ -0,0 +1,632 @@ +#include "ConfigParser.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ + +#include + +#endif + +using namespace std; + +/** 清理字符串前后空格 */ +void trim(std::string &s) { + auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { + return std::isspace(c); + }); + auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) { + return std::isspace(c); + }).base(); + s = (wsback <= wsfront) ? std::string() : std::string(wsfront, wsback); +} + +CPStatus configFromIni(const std::string &path, std::unordered_map &config) { + CPStatus result = CPSUCCESS; + //打开文件 + ifstream file(path); + if (!file.is_open()) { + result = CPFILE; + return result; + } + // 一次性读取整个文件(减少I/O操作) + file.seekg(0, std::ios::end); + size_t size = file.tellg(); + file.seekg(0, std::ios::beg); + string m_buffer; + m_buffer.resize(size); + file.read(&m_buffer[0], static_cast(size)); + //处理全部数据 + config.clear(); + std::string line; + line.reserve(256); // 预分配行缓存 + for (auto it = m_buffer.cbegin(); it != m_buffer.cend();) { + // 快速跳过空白行 + while (it != m_buffer.cend() && (*it == '\r' || *it == '\n')) ++it; + if (it == m_buffer.cend()) break; + + // 获取行内容 + line.clear(); + while (it != m_buffer.cend() && *it != '\r' && *it != '\n' && *it != '\0') { + line += *it++; + } + //处理行内容 + // 跳过空行和注释 + if (line.empty() || line[0] == ';' || line[0] == '#') { + ++it; + continue; + } + + // 分割键值对 + size_t eq_pos = line.find('='); + if (eq_pos != std::string::npos) { + std::string key = line.substr(0, eq_pos); + std::string value = line.substr(eq_pos + 1); + trim(key); + trim(value); + if (!key.empty()) { + config[key] = value; // 移动语义自动优化 + } + } + } + return result; +} + +void UDPSocketBase::create_socket() { + while (true) { + sockfd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +#if defined(_WIN32) + if (sockfd_ != INVALID_SOCKET_VALUE){ + break; + } +#else + if (sockfd_ >= 0) { + break; + } +#endif + } +} + +void UDPSocketBase::create_socket(socket_t socket) { + sockfd_ = socket; +} + +void UDPSocketBase::set_nonblock(bool enable) { + u_long ctl = enable ? 1 : 0; +#if defined(_WIN32) + ioctlsocket(sockfd_, FIONBIO, &ctl) == 0; +#else + ioctl(sockfd_, FIONBIO, &ctl) == 0; +#endif + is_nonblock_ = enable; +} + +void UDPSocketBase::bind_socket(const sockaddr_in &addr) const { + if (bind(sockfd_, (struct sockaddr *) &addr, sizeof(addr)) != 0) { + throw std::runtime_error("Bind failed"); + } +} + +UDPSocketBase::~UDPSocketBase() { +#if defined(_WIN32) + int ret = closesocket(sockfd_); +#else + int ret = close(sockfd_); +#endif + sockfd_ = (~0); +} + +UDPSender::UDPSender(socket_t socket, bool nonblock) { + if (socket != INVALID_SOCKET_VALUE) { + create_socket(socket); + } + else { + create_socket(); + } + set_nonblock(nonblock); +} + +void UDPSender::set_destination(const std::string &ip, uint16_t port) { + dstIp = ip; + dstPort = port; + dest_addr_.sin_family = AF_INET; + dest_addr_.sin_port = htons(port); +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x600 + dest_addr_.sin_addr.S_un.S_addr = inet_addr(ip.c_str()); +#else + inet_pton(AF_INET, ip.c_str(), &dest_addr_.sin_addr); +#endif +} + +void UDPSender::set_source(const std::string &ip, uint16_t port) { + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x600 + addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str()); +#else + inet_pton(AF_INET, ip.c_str(), &addr.sin_addr); +#endif + bind_socket(addr); +} + +size_t UDPSender::send(const std::vector &data) { + auto sent = sendto(sockfd_, + reinterpret_cast(data.data()), + static_cast(data.size()), + 0, + (struct sockaddr *) &dest_addr_, + sizeof(dest_addr_)); + + if (sent == SOCKET_ERROR) { +// throw std::runtime_error("Send failed"); + printf("Send failed\n"); + } + return static_cast(sent); +} + +size_t UDPSender::send(const std::string &data) { + auto sent = sendto(sockfd_, + reinterpret_cast(data.data()), + static_cast(data.size()), + 0, + (struct sockaddr *) &dest_addr_, + sizeof(dest_addr_)); + + if (sent == SOCKET_ERROR) { +// throw std::runtime_error("Send failed"); + printf("Send failed\n"); + + } + return static_cast(sent); +} + +size_t UDPSender::send(uint8_t *data, size_t size) { + int nleft, nwritten; + const char *ptr; + + ptr = (char *) data; /* can't do pointer arithmetic on void* */ + nleft = size; + while (nleft > 0) { +#if defined(_WIN32) + if ((nwritten = sendto(sockfd_, ptr, nleft, 0, (const struct sockaddr*)&dest_addr_, sizeof(dest_addr_))) <= 0) +#else + if ((nwritten = sendto(sockfd_, ptr, nleft, MSG_NOSIGNAL, (const struct sockaddr *) &dest_addr_, + sizeof(struct sockaddr))) <= 0) +#endif + { + if (!(errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) + return -1; /* error */ + else + continue; + } + + nleft -= nwritten; + ptr += nwritten; + } + + return (size); +} + + +UDPReceiver::UDPReceiver(uint16_t port, bool nonblock, size_t buffer_size) + : buffer_(buffer_size), running_(false), srcPort(port) { + create_socket(); + + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + bind_socket(addr); + set_nonblock(nonblock); + this->isOnline.store(false); +} + +void UDPReceiver::start_receiving(const UDPReceiver::ReceiveCallback &callback) { + running_ = true; + receiver_thread_ = std::thread([this, callback]() { + while (running_.load(std::memory_order_relaxed)) { + try { + auto result = receive(); + ssize_t size = result.first; + auto &from = result.second; + if (size > 0) { + lastTime_ = std::chrono::steady_clock::now(); + std::vector data(buffer_.begin(), + buffer_.begin() + size); + callback(data, from.first, from.second); + } + } + catch (const std::runtime_error &e) { + if (is_nonblock_) continue; +// cerr << e.what() << endl; + } + } + }); + online_thread_ = std::thread([this]() { + while (running_.load(std::memory_order_relaxed)) { + this_thread::sleep_for(std::chrono::seconds(1)); + if (!isOnline.load()) { + continue; + } + auto time = std::chrono::steady_clock::now() - lastTime_; + if (std::chrono::duration_cast(time).count() > 5) { + isOnline.store(false); + } + } + }); +} + +void UDPReceiver::stop_receiving() { + running_.store(false); + if (receiver_thread_.joinable()) { + receiver_thread_.join(); + } + if (online_thread_.joinable()) { + online_thread_.join(); + } +} + +std::pair> UDPReceiver::receive() { + sockaddr_in from_addr{}; + socklen_t from_len = sizeof(from_addr); +#ifdef _WIN32 + DWORD timeout = UDPTimeout*1000; // 3秒(单位:毫秒) + setsockopt(sockfd_, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); +#else + struct timeval tv; + tv.tv_sec = UDPTimeout; // 秒 + tv.tv_usec = 0; // 微秒 + setsockopt(sockfd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); +#endif + auto size = recvfrom(sockfd_, + reinterpret_cast(buffer_.data()), + static_cast(buffer_.size()), + 0, + (struct sockaddr *) &from_addr, + &from_len); + + if (size == SOCKET_ERROR) { + throw std::runtime_error("Receive Timeout"); + } + + return {size, {from_addr, from_len}}; +} + +UDPReceiver::~UDPReceiver() { + stop_receiving(); +} + +LogL::LogL(LogType logType, bool isSync, LogLevel level, std::string logPath) : m_isSync(isSync), m_logType(logType), + m_logPath(std::move(logPath)), + m_file(nullptr), + m_thread(nullptr), m_sender(nullptr), + m_logLevel(level) { + + switch (m_logType) { + case LTStd: { + break; + } + case LTTxt: { + //创建文件 + myDateTime now; + std::string dirName; + if (m_logPath.empty()) { + dirName = "log"; + } + else { + dirName = m_logPath; + } + myMkdir(dirName.c_str()); + std::string fileName = dirName + "/" + now.toDate() + ".txt"; + m_file = new std::ofstream(fileName, std::ios::app); + m_today = now.day; + break; + } + case LTUdp: { + m_sender.reset(new UDPSender); + std::vector parts; + std::stringstream ss(m_logPath); + std::string part; + // 按逗号分割字符串 + while (std::getline(ss, part, ':')) { + parts.push_back(part); + } + if (parts.size() == 3) { + m_sender->set_destination(parts[0], stoi(parts[1])); + m_sender->set_source(parts[0], stoi(parts[2])); + } + else if (parts.size() == 2) { + m_sender->set_destination(parts[0], stoi(parts[1])); + } + else { + throw std::runtime_error("UDP Path Error"); + } + break; + } + case LTTcp: { + + break; + } + } + if (!m_isSync) { + //运行日志线程 + m_thread_run = true; + m_thread = new std::thread(&LogL::logThread, this); + } +} + +LogL::~LogL() { + //停止线程 + while (m_thread) { + if (m_log_queue.empty()) { + cout << "已清空日志队列" << endl; + break; + } + cout << "正在写入剩余日志:" << m_log_queue.size() << endl; + this_thread::sleep_for(chrono::milliseconds(300)); + } + if (m_thread) { + //设置结束并等待线程结束 + m_thread_run = false; + m_cond_empty.notify_all(); + if (m_thread->joinable()) { + m_thread->join(); + } + delete m_thread; + m_thread = nullptr; + } + //关闭文件 + m_mtx.lock(); + if (m_file) { + m_file->flush(); + m_file->close(); + delete m_file; + m_file = nullptr; + } + m_mtx.unlock(); + cout << "日志析构函数完成" << endl; +} + +void LogL::logThread() { + std::string str; + while (m_thread_run) { + //如果队列为空,等待 + if (m_log_queue.empty()) { + std::unique_lock lock(m_mtx); + m_cond_empty.wait(lock); + continue; + } + //取出队列头并出栈 + str = m_log_queue.front(); + m_log_queue.pop(); + m_cond_full.notify_one(); + switch (m_logType) { + case LTStd: + cout << str << endl; + break; + case LTTxt: { + if (m_file == nullptr) { + std::cerr << "logThread():日志文件打开失败" << endl; + break; + } + *m_file << str << endl; + break; + } + case LTUdp: + try { + m_sender->send(str); + } + catch (const std::runtime_error &e) { + std::cerr << e.what() << endl; + } + break; + case LTTcp: + break; + } + + } +} + +void LogL::debug(const std::string &str) { + log(str, LLDebug); +} + +void LogL::info(const std::string &str) { + log(str, LLInfo); +} + +void LogL::warn(const std::string &str) { + log(str, LLWarn); +} + +void LogL::error(const std::string &str) { + log(str, LLError); +} + +void LogL::log(const std::string &str, LogLevel level) { + if (level < m_logLevel) { + return; + } + string tmpLever; + switch (level) { + case LLDebug: + tmpLever = " [debug]:"; + break; + case LLInfo: + tmpLever = " [info]:"; + break; + case LLWarn: + tmpLever = " [warn]:"; + break; + case LLError: + tmpLever = " [error]:"; + break; + } + myDateTime now; + if (m_logType == LTTxt) { + //逾期更新日志文件 + m_mtx.lock(); + if (now.day != m_today) { + if (m_file) { + m_file->flush(); + m_file->close(); + delete m_file; + m_file = nullptr; + } + std::string dirName; + if (m_logPath.empty()) { + dirName = "log"; + } + else { + dirName = m_logPath; + } + myMkdir(dirName.c_str()); + std::string fileName = + dirName + "/" + now.toDate() + ".txt"; + m_file = new std::ofstream(fileName, std::ios::app); + } + m_mtx.unlock(); + } + std::string logInfo = now.toDateTime() + tmpLever + str; + if (m_isSync) { + switch (m_logType) { + case LTStd: + cout << logInfo << endl; + break; + case LTTxt: { + if (m_file == nullptr) { + std::cerr << "日志文件打开失败" << endl; + break; + } + *m_file << logInfo << endl; + break; + } + case LTUdp: + try { + m_sender->send(logInfo); + } + catch (const std::runtime_error &e) { + std::cerr << e.what() << endl; + } + break; + case LTTcp: + break; + } + } + else { + if (m_log_queue.size() >= 4096) { + std::unique_lock lock(m_mtx); + m_cond_full.wait(lock); + } + m_mtx.lock(); + m_log_queue.push(logInfo); + m_mtx.unlock(); + m_cond_empty.notify_all(); + } +} + + +bool createPath(const char *path) { + struct stat st = {0}; + if (stat(path, &st) == -1) { +#if WIN32 + if (mkdir(path) == 0) { +#else + if (mkdir(path, 0777) == 0) { +#endif + return true; + } + else { + std::cerr << "Failed to create directory: " << path << std::endl; + return false; + } + } + return true; // Directory already exists +} + +bool myMkdir(const char *path) { + char buffer[1024]; + char *p = buffer; + const char *sep = "/"; + + strcpy(buffer, path); + while ((p = strchr(p, *sep)) != nullptr) { + *p = '\0'; + if (!createPath(buffer)) { + return false; + } + *p = *sep; + p++; + } + createPath(buffer); + return true; +} + +myDateTime::myDateTime() { + auto now = std::chrono::system_clock::now(); + std::time_t time1 = std::chrono::system_clock::to_time_t(now); + std::tm *tm1 = std::localtime(&time1); + year = tm1->tm_year + 1900; + month = tm1->tm_mon + 1; + day = tm1->tm_mday; + hour = tm1->tm_hour; + minute = tm1->tm_min; + second = tm1->tm_sec; +} + +std::string myDateTime::toDate() const { + string date = to_string(year) + "-"; + if (month < 10) { + date += "0" + to_string(month) + "-"; + } + else { + date += to_string(month) + "-"; + } + if (day < 10) { + date += "0" + to_string(day); + } + else { + date += to_string(day); + } + return date; +} + +std::string myDateTime::toTime() const { + string time; + if (hour < 10) { + time += "0" + to_string(hour) + ":"; + } + else { + time += to_string(hour) + ":"; + } + if (minute < 10) { + time += "0" + to_string(minute) + ":"; + } + else { + time += to_string(minute) + ":"; + } + if (second < 10) { + time += "0" + to_string(second); + } + else { + time += to_string(second); + } + return time; +} + +std::string myDateTime::toDateTime() const { + string dateTime = toDate() + " " + toTime(); + return dateTime; +} diff --git a/Udp/ConfigParser.h b/Udp/ConfigParser.h new file mode 100644 index 0000000..8ef7f86 --- /dev/null +++ b/Udp/ConfigParser.h @@ -0,0 +1,268 @@ +#ifndef CONFIGPARSER_H +#define CONFIGPARSER_H +/** + * 配置文件解析模块 + */ +#include +#include + +#include + + +// 平台检测 +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +//定义UDP接收线程超时时间(秒) +#define UDPTimeout (3) + +// 平台兼容类型定义 +#ifdef _WIN32 +using socklen_t = int; +using socket_t = SOCKET; +constexpr socket_t INVALID_SOCKET_VALUE = INVALID_SOCKET; +#else +using socket_t = int; +constexpr socket_t INVALID_SOCKET_VALUE = -1; +#define SOCKET_ERROR (-1) +#endif + +// Windows平台初始化 +#ifdef _WIN32 +class WSAInitializer { +public: + WSAInitializer() { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + throw std::runtime_error("WSAStartup failed"); + } + } + + ~WSAInitializer() { + WSACleanup(); + } +}; + +static WSAInitializer wsa_initializer; +#endif + +/** 状态码 */ +enum CPStatus { + CPSUCCESS = 0,//成功 + CPFILE = -1//文件错误 +}; + +/** + * 读取ini类型的配置文件 + * @param path 配置文件路径 + * @param config 存储配置文件的unordered_map容器 + * @return 状态码 + */ +CPStatus configFromIni(const std::string &path, std::unordered_map &config); + + +/** + * UDP基类 + */ +class UDPSocketBase { +protected: + UDPSocketBase() : sockfd_(INVALID_SOCKET_VALUE), is_nonblock_(false) {} + + /** 创建套接字 */ + void create_socket(); + + void create_socket(socket_t socket); + + /** 设置是否阻塞 */ + void set_nonblock(bool enable); + + /** 建立链接 */ + void bind_socket(const sockaddr_in &addr) const; + +public: + virtual ~UDPSocketBase(); + + socket_t sockfd_; +protected: + bool is_nonblock_; +}; + +/** + * UDP发送端 + */ +class UDPSender : public UDPSocketBase { +public: + explicit UDPSender(socket_t socket = INVALID_SOCKET_VALUE, bool nonblock = false); + + /** 设置目的地址 */ + void set_destination(const std::string &ip, uint16_t port); + + /** 设置源地址 */ + void set_source(const std::string &ip, uint16_t port); + + /** 发送 */ + size_t send(const std::vector &data); + + size_t send(uint8_t *data, size_t size); + + size_t send(const std::string &data); + + std::string dstIp; + uint16_t dstPort; + +private: + sockaddr_in dest_addr_{}; +}; + +/** + * UDP接收端 + */ +class UDPReceiver : public UDPSocketBase { +public: + /** 处理接收数据函数模板 */ + using ReceiveCallback = std::function &, sockaddr_in, socklen_t)>; + + /** 设置接收端口 */ + explicit UDPReceiver(uint16_t port, bool nonblock = false, size_t buffer_size = 4096); + + /** 开始接收线程 */ + void start_receiving(const ReceiveCallback &callback); + + /** 停止接收线程 */ + void stop_receiving(); + + ~UDPReceiver() override; + + uint16_t srcPort; + /*UDP接收是否在线*/ + std::atomic isOnline; + +private: + /** 接收函数 */ + std::pair> receive(); + +private: + std::vector buffer_; + std::atomic running_; + std::thread receiver_thread_; + std::thread online_thread_; + std::chrono::time_point lastTime_; +}; + + +enum LogType { + LTStd,//日志输出到控制台 + LTTxt,//日志输出到文本文件 + LTUdp,//日志输出到UDP + LTTcp,//日志输出到TCP +}; +enum LogLevel { + LLDebug,//调试 + LLInfo,//一般 + LLWarn,//警告 + LLError,//错误 +}; + + +class LogL { +public: + /** + * 日志构造函数 + * @param logType 日志输出类型 + * @param isSync true:同步/false:异步 + * @param logPath 输出地址(LTStd:无;LTTxt:填写路径;LTUdp:填写ip端口) + */ + explicit LogL(LogType logType, bool isSync = true, LogLevel level = LLDebug, std::string logPath = ""); + + + ~LogL(); + + /** + * 调试日志 + * @param str 日志内容 + */ + void debug(const std::string &str); + + void info(const std::string &str); + + void warn(const std::string &str); + + void error(const std::string &str); + +private: + + void logThread(); + + void log(const std::string &str, LogLevel level); + + +private: + std::thread *m_thread; + bool m_thread_run; + bool m_isSync;//是否同步 + LogType m_logType;//输出类型 + LogLevel m_logLevel;//输出级别 + std::string m_logPath;//输出地址 + std::queue m_log_queue;//日志容器 + std::mutex m_mtx; + std::condition_variable m_cond_empty;//空时停止 + std::condition_variable m_cond_full;//满时停止 + std::ofstream *m_file; + int32_t m_today; + std::unique_ptr m_sender; +}; + + +/** + * 创建目录 + * @param path 路径 + * @return bool是否创建成功 + */ +bool myMkdir(const char *path); + +/** + * 获取当前日期时间 + */ +class myDateTime { +public: + myDateTime(); + + int32_t year; + int32_t month; + int32_t day; + int32_t hour; + int32_t minute; + int32_t second; + + std::string toDate() const; + + std::string toTime() const; + + std::string toDateTime() const; +}; + + +#endif //CONFIGPARSER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..7afb6e2 --- /dev/null +++ b/main.cpp @@ -0,0 +1,83 @@ +#include + +#include "GlobalDefs.h" +#include "ComCtrl.h" +#include "Noncanonical.h" + +using namespace std; + +// 全局命令存储 +std::vector historyList{}; +Send Sender; + + +int main() { + // 设置非规范终端模式 + set_noncanonical_mode(); + + if (!Sender.isRunning()) { + return 0; + } + + cout << "CP Ctrl Console (v1.0)\nType 'exit' to quit" << endl; + cout << "Type 'help' for available commands" << endl; + + while (true) { + string input = readLine("> "); + + if (input.empty()) continue; + + // 处理输入 + vector args; + string token; + for (char c: input) { + if (isspace(c)) { + if (!token.empty()) { + args.push_back(token); + token.clear(); + } + } + else { + token += c; + } + } + if (!token.empty()) args.push_back(token); + + if (args.empty()) continue; + + string command = toLower(args[0]); + + if (command == "exit") { + cout << "Exiting..." << endl; + break; + } + else if (command == "setant") { + setantCommand(args); + } + else if (command == "seteph") { + setephCommand(args); + } + else if (command == "query") { + queryCommand(args); + } + else if (command == "list") { + listCommand(); + } + else if (command == "help") { + cout << "Available commands:\n" + << " setant - SetAnt key-value pair\n" + << " seteph - SetEph key-value pair\n" + << " query - query type-key value\n" + << " list - history\n" + << " exit - Exit program\n" + << " help - Show this help\n\n" + << "Tab completion available for commands and keys" << endl; + } + else { + cout << "Invalid command: " << command << endl; + cout << "Type 'help' for available commands" << endl; + } + } + + return 0; +} \ No newline at end of file