#ifndef SERIAL_H #define SERIAL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "serialib.h" using namespace std::literals::chrono_literals; namespace ranges = std::ranges; namespace views = std::views; namespace serial { template struct _StrongType {}; template struct _StrongType { using Type = T; }; template struct _StrongType { using Type = decltype(true ? std::declval() : std::declval()); }; template struct _StrongType { using Type = typename _StrongType::Type>::Type; }; template using _strongType_t = typename _StrongType::Type; template struct _IsCastable {}; template struct _IsCastable { static constexpr bool value = true; using Type = T; }; template struct _IsCastable { using __TRUE = char; using __FALSE = struct { char _[2]; }; static consteval __TRUE __TEST(U); static consteval __FALSE __TEST(...); static constexpr bool value = sizeof(__TEST(std::declval())) == sizeof(__TRUE); using Type = std::conditional_t; }; template struct _IsCastable { static constexpr bool value = _IsCastable::Type>::value; using Type = std::conditional_t::Type, void>; }; template concept _SupportString = requires { _IsCastable::value; }; template <_SupportString T> [[maybe_unused]] std::string _to_string(T&& str) { if constexpr (std::is_same_v, std::string>) { return str; } else if constexpr (std::is_same_v, std::string_view>) { return std::move(std::string(str.data())); } else if constexpr (std::is_same_v, const char*>) { return std::string(str); } } static std::vector GetUsbPorts() { #if defined(_WIN32) || defined(_WIN64) std::vector portArray; std::string comname; std::string showname; ranges::for_each(views::iota(1, 256), [&](int i) { std::format_to(std::back_inserter(comname), "\\\\.\\COM{}", i); std::format_to(std::back_inserter(showname), "COM{}", i); const HANDLE m_handle = ::CreateFileA( comname.c_str(), static_cast(GENERIC_WRITE) | GENERIC_READ, 0U, nullptr, OPEN_EXISTING, 0U, nullptr); if (m_handle != INVALID_HANDLE_VALUE) { portArray.emplace_back(showname); CloseHandle(m_handle); }else if(GetLastError() == ERROR_ACCESS_DENIED){ portArray.emplace_back(showname); } comname.clear(); showname.clear(); }); return portArray; #elif defined(__linux__) #endif } enum class [[maybe_unused]] SerialErrorCode { SUCCESS, TIMEOUT, SETTIMEOUTERROR, WRITEINGERROR, READINGERROR, }; enum SerialDataBits { DATABIT_5 = 0, DATABIT_6, DATABIT_7, DATABIT_8, DATABIT_16, }; enum SerialStopBits { STOPBIT_1 = 0, STOPBIT_1_5, STOPBIT_2, }; enum SerialParity { SERIAL_PARITY_NONE = 0, SERIAL_PARITY_EVEN, SERIAL_PARITY_ODD, SERIAL_PARITY_MARK, SERIAL_PARITY_SPACE, }; class Serial { private: serialib ser; const char* endChar = "\r\n"; std::function logCallBack; bool removeEcho = true; // 剔除回显 public: Serial() = default; ~Serial() { CloseDevice(); } Serial(const Serial& other) = delete; Serial(Serial&& other) = delete; Serial& operator=(const Serial& other) = delete; Serial& operator=(Serial&& other) = delete; auto SetRemoveEcho(bool remove) { removeEcho = remove; } auto SetDtr(bool flag) { return flag ? ser.setDTR() : ser.clearDTR(); } auto SetRts(bool flag) { return flag ? ser.setRTS() : ser.clearRTS(); } static std::string GetTimeNow() { auto now = std::chrono::system_clock::now(); auto now_c = std::chrono::system_clock::to_time_t(now); char buffer[32]; auto _ = ctime_s(buffer, 32, &now_c); return buffer; } bool IsOpen() { return ser.isDeviceOpen(); } template <_SupportString T> bool OpenDevice(T portName, unsigned int bauds = 115200, int delayTime = 0, SerialDataBits dataBits = SerialDataBits::DATABIT_8, SerialStopBits stopBits = SerialStopBits::STOPBIT_1, SerialParity parity = SerialParity::SERIAL_PARITY_NONE) { #if defined(_WIN32) || defined(__WIN64) std::string reallyPortName; std::format_to(std::back_inserter(reallyPortName), "\\\\.\\{}", portName); #elif defined(__linux__) std::string reallyPortName = std::string(portName); #endif std::this_thread::sleep_for(std::chrono::milliseconds(delayTime)); if (ser.isDeviceOpen()) ser.closeDevice(); int code = ser.openDevice(reallyPortName.c_str(), bauds, static_cast<::SerialDataBits>(dataBits), static_cast<::SerialParity>(parity), static_cast<::SerialStopBits>(stopBits)); if (code == 1) { return true; } else { return false; } } void Log(const std::string& log) const { if (logCallBack) { auto msg = GetTimeNow() + " " + log; logCallBack(msg); } } void SetLogCallBack(const std::function& callBack) { logCallBack = callBack; } void CloseDevice() { if (!ser.isDeviceOpen()) return; ser.closeDevice(); } template <_SupportString T> std::expected DelayGetResponse(int delayTime, T command, int timeout = 50) { std::this_thread::sleep_for(std::chrono::milliseconds(delayTime)); return GetAtResponse(command, timeout); } template bool GetAtUntilRepeat(T command, Args... args) { std::stringstream ss; int i = 0; while (i++ < repeatTime) { ss.str(""); ss << "Count: " << i << "\n"; Log(ss.str()); std::this_thread::sleep_for(std::chrono::milliseconds(delayTime)); if (GetAtUntil(command, args...)) return true; } return false; } template <_SupportString T> std::expected GetAtResponse(T command, int timeout = 50) { ser.flushReceiver(); std::string reallyCommand = std::string(command) + endChar; ser.writeString(reallyCommand.c_str()); Log("Send: " + reallyCommand); std::this_thread::sleep_for(10ms); auto availableSize = ser.available(); auto buffer = std::make_unique(availableSize + 1); std::memset(buffer.get(), 0, availableSize); auto size = ser.readBytes(buffer.get(), availableSize, timeout); if (size > 0) { buffer[size] = '\0'; std::string response = std::string(buffer.get()); Log("Receive: " + response); if (removeEcho) response.replace(0, reallyCommand.length(), ""); return response; } return std::unexpected(SerialErrorCode::TIMEOUT); } template <_SupportString T> auto GetAtResponseRepeat(T command, int timeout = 200, int repeatTime = 1) -> void { for (int i = 0; i <= repeatTime; i++) { auto _ = GetAtResponse(command, timeout); } } template bool GetAtUntil(T command, Args... expect) { auto endTime = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout); ser.flushReceiver(); std::string resp; std::string reallyCommand = std::string(command) + endChar; ser.writeString(reallyCommand.c_str()); Log("Send : " + reallyCommand); while (std::chrono::system_clock::now() < endTime) { std::this_thread::sleep_for(10ms); auto availableSize = ser.available(); auto buffer = std::make_unique(availableSize + 1); auto size = ser.readBytes(buffer.get(), availableSize, timeout); buffer[size] = '\0'; auto str = std::string(buffer.get()); if (size > 0){ Log("Receive: " + str); resp += str; } if (((str.find(expect) != std::string::npos) && ...)) { return true; } } return false; } template bool ReadUntil(Args... args){ auto end_time = std::chrono::system_clock::now() + 1s; std::string resp; while(std::chrono::system_clock::now() < end_time){ std::this_thread::sleep_for(10ms); auto available_size = ser.available(); auto buffer = std::unique_ptr{available_size + 1}; auto read_size = ser.readBytes(buffer.get(), available_size, timeout); buffer[read_size] = '\0'; auto str = std::string(buffer.get()); resp += str; if(read_size > 0){ Log("Read: " + str); } if(((resp.find(args) != std::string::npos) && ...)){ return true; } } } template std::string GetAtUntilAndReturn(T command, Args... expect) { auto endTime = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout); ser.flushReceiver(); std::string resp = ""; std::string reallyCommand = std::string(command) + endChar; ser.writeString(reallyCommand.c_str()); Log("Send : " + reallyCommand); while (std::chrono::system_clock::now() < endTime) { std::this_thread::sleep_for(10ms); auto availableSize = ser.available(); auto buffer = std::make_unique(availableSize + 1); auto size = ser.readBytes(buffer.get(), availableSize, timeout); buffer[size] = '\0'; auto str = std::string(buffer.get()); resp += str; if (size > 0) Log("Receive: " + str); if (((resp.find(expect) != std::string::npos) && ...)) { return resp; } } return resp; } }; } // namespace serial #endif // SERIAL_H