322 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef SERIAL_H
 | 
						|
#define SERIAL_H
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <chrono>
 | 
						|
#include <expected>
 | 
						|
#include <format>
 | 
						|
#include <functional>
 | 
						|
#include <iostream>
 | 
						|
#include <ranges>
 | 
						|
#include <sstream>
 | 
						|
#include <string>
 | 
						|
#include <string_view>
 | 
						|
#include <thread>
 | 
						|
#include <type_traits>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include "serialib.h"
 | 
						|
 | 
						|
using namespace std::literals::chrono_literals;
 | 
						|
namespace ranges = std::ranges;
 | 
						|
namespace views = std::views;
 | 
						|
 | 
						|
namespace serial {
 | 
						|
	template <typename... Types> struct _StrongType {};
 | 
						|
 | 
						|
	template <typename T> struct _StrongType<T> {
 | 
						|
		using Type = T;
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename Ta, typename Tb> struct _StrongType<Ta, Tb> {
 | 
						|
		using Type = decltype(true ? std::declval<Ta>() : std::declval<Tb>());
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename T, typename... Types> struct _StrongType<T, Types...> {
 | 
						|
		using Type =
 | 
						|
			typename _StrongType<T, typename _StrongType<Types...>::Type>::Type;
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename... Types>
 | 
						|
	using _strongType_t = typename _StrongType<Types...>::Type;
 | 
						|
 | 
						|
	template <typename... Args> struct _IsCastable {};
 | 
						|
 | 
						|
	template <typename T> struct _IsCastable<T> {
 | 
						|
		static constexpr bool value = true;
 | 
						|
		using Type = T;
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename T, typename U> struct _IsCastable<T, U> {
 | 
						|
		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<T>())) == sizeof(__TRUE);
 | 
						|
		using Type = std::conditional_t<value, U, void>;
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename T, typename... Args> struct _IsCastable<T, Args...> {
 | 
						|
		static constexpr bool value =
 | 
						|
			_IsCastable<T, typename _IsCastable<Args...>::Type>::value;
 | 
						|
		using Type =
 | 
						|
			std::conditional_t<value, typename _IsCastable<Args...>::Type, void>;
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename T>
 | 
						|
	concept _SupportString = requires {
 | 
						|
		_IsCastable<T, const char*, char*, std::string, std::string_view>::value;
 | 
						|
	};
 | 
						|
	template <_SupportString T> [[maybe_unused]] std::string _to_string(T&& str) {
 | 
						|
		if constexpr (std::is_same_v<std::decay_t<T>, std::string>) {
 | 
						|
			return str;
 | 
						|
		}
 | 
						|
		else if constexpr (std::is_same_v<std::decay_t<T>, std::string_view>) {
 | 
						|
			return std::move(std::string(str.data()));
 | 
						|
		}
 | 
						|
		else if constexpr (std::is_same_v<std::decay_t<T>, const char*>) {
 | 
						|
			return std::string(str);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	static std::vector<std::string> GetUsbPorts() {
 | 
						|
#if defined(_WIN32) || defined(_WIN64)
 | 
						|
		std::vector<std::string> 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<DWORD>(GENERIC_WRITE) | GENERIC_READ,
 | 
						|
				0U, nullptr, OPEN_EXISTING, 0U, nullptr);
 | 
						|
			if (m_handle != INVALID_HANDLE_VALUE) {
 | 
						|
				portArray.push_back(showname);
 | 
						|
				CloseHandle(m_handle);
 | 
						|
			}
 | 
						|
			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<void(const std::string&)> 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<void(const std::string&)>& callBack) {
 | 
						|
			logCallBack = callBack;
 | 
						|
		}
 | 
						|
		void CloseDevice() {
 | 
						|
			if (!ser.isDeviceOpen())
 | 
						|
				return;
 | 
						|
			ser.closeDevice();
 | 
						|
		}
 | 
						|
 | 
						|
		template <_SupportString T>
 | 
						|
		std::expected<std::string, SerialErrorCode>
 | 
						|
			DelayGetResponse(int delayTime, T command, int timeout = 50) {
 | 
						|
			std::this_thread::sleep_for(std::chrono::milliseconds(delayTime));
 | 
						|
			return GetAtResponse(command, timeout);
 | 
						|
		}
 | 
						|
 | 
						|
		template <int repeatTime = 5, int delayTime = 200, int timeout = 200,
 | 
						|
			_SupportString T, _SupportString... Args>
 | 
						|
		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<timeout>(command, args...))
 | 
						|
					return true;
 | 
						|
			}
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		template <_SupportString T>
 | 
						|
		std::expected<std::string, SerialErrorCode>
 | 
						|
			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<char[]>(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 <int timeout = 200, _SupportString T, _SupportString... Args>
 | 
						|
		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<char[]>(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 <int timeout = 200, _SupportString T, _SupportString... Args>
 | 
						|
		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<char[]>(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
 |