diff --git a/SerialCOM/Source/SERIALCOM/Private/SerialCom.cpp b/SerialCOM/Source/SERIALCOM/Private/SerialCom.cpp index a988a53..4fdb9b7 100644 --- a/SerialCOM/Source/SERIALCOM/Private/SerialCom.cpp +++ b/SerialCOM/Source/SERIALCOM/Private/SerialCom.cpp @@ -2,10 +2,18 @@ #include "SerialCom.h" +#if PLATFORM_WINDOWS #include "Windows/AllowWindowsPlatformTypes.h" #include "Windows/MinWindows.h" #include "Windows/HideWindowsPlatformTypes.h" - +#include //SetupDiGetClassDevs Setup* +#include //GUID +#include +#include +#ifndef GUID_DEVINTERFACE_COMPORT +DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73); +#endif +#endif #define BOOL2bool(B) B == 0 ? false : true @@ -16,12 +24,175 @@ USerialCom* USerialCom::OpenComPortWithFlowControl(bool& bOpened, int32 Port, in return Serial; } +TArray USerialCom::GetAllSerialPortDevicesAndPortNumbers() +{ + TArray DeviceNameToPort; + //Get all serial ports name and port +#if PLATFORM_WINDOWS + // https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinfo + + bool bRet = false; + struct SerialPortInfo { + int32 port = -1; + std::wstring portName; + std::wstring description; + }; + SerialPortInfo m_serialPortInfo; + TArray< SerialPortInfo> portInfoList; + + std::wstring strFriendlyName; + std::wstring strPortName; + + HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; + + // Return only devices that are currently present in a system + // The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID + // {86E0D1E0-8089-11D0-9CE4-08003E301F73} + hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (INVALID_HANDLE_VALUE != hDevInfo) + { + SP_DEVINFO_DATA devInfoData; + // The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA) + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++) + { + // get port name + TCHAR portName[256]; + HKEY hDevKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (INVALID_HANDLE_VALUE != hDevKey) + { + DWORD dwCount = 255; // DEV_NAME_MAX_LEN + RegQueryValueEx(hDevKey, _T("PortName"), NULL, NULL, (BYTE*)portName, &dwCount); + RegCloseKey(hDevKey); + } + + // get friendly name + TCHAR fname[256]; + SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)fname, + sizeof(fname), NULL); + + strPortName = portName; + strFriendlyName = fname; + // remove (COMxx) + strFriendlyName = strFriendlyName.substr(0, strFriendlyName.find(TEXT("(COM"))); + + m_serialPortInfo.portName = strPortName; + m_serialPortInfo.description = strFriendlyName; + portInfoList.Add(m_serialPortInfo); + } + + if (ERROR_NO_MORE_ITEMS == GetLastError()) + { + bRet = true; // no more item + } + } + + SetupDiDestroyDeviceInfoList(hDevInfo); + + if (bRet) + { + for (auto it : portInfoList) + { + m_serialPortInfo.portName = it.portName; + int port = strPortName.size() - 1; + while (port >= 0 && isdigit(strPortName[port])) + { + port--; + } + std::wstring LocalString = strPortName.substr(port + 1); + m_serialPortInfo.port = stoi(LocalString); + + DeviceNameToPort.Add(FSerialPortInfo(m_serialPortInfo.port, m_serialPortInfo.portName.data(), it.description.data())); + } + return DeviceNameToPort; + } +#else +#endif + return TArray(); +} + +bool USerialCom::FindSerialPortDevicePortNumber(FString DeviceName, int32& FindComPort, int32 FindFlags) +{ + TArray LocalFindSerialPort = FindAllSerialPortDevicePortInfo(DeviceName, FindFlags); + if (LocalFindSerialPort.Num() > 0) + { + FindComPort = LocalFindSerialPort[0].Port; + return true; + } + return false; +} + ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// +TArray USerialCom::FindAllSerialPortDevicePortInfo(FString DeviceName, int32 FindFlags) +{ + TArray ReturnValue; + if (FindFlags > (int32)ESerialDevicesFindFlags::ESDFF_RegularExpressionMatching) + { + UE_LOG(LogTemp, Error, TEXT("FindFlags is greater than %d,this is a bad value. FindFlags: %d"), ESerialDevicesFindFlags::ESDFF_RegularExpressionMatching, FindFlags); + ensure(0); + return ReturnValue; + } + TArray< FSerialPortInfo> LocalFindSerialPort = GetAllSerialPortDevicesAndPortNumbers(); + if (LocalFindSerialPort.Num() < 0) + { + return ReturnValue; + } + for (auto it : LocalFindSerialPort) + { + //判断是否使用正则表达式匹配字符串 + if (FindFlags == (int32)ESerialDevicesFindFlags::ESDFF_RegularExpressionMatching) + { + FRegexPattern Pattern(*DeviceName); + FRegexMatcher Matcher(Pattern, it.Description); + Matcher.SetLimits(0, it.Description.Len()); + if (Matcher.FindNext()) + { + ReturnValue.Add(it); + } + } + else + { + //判断使用全字符匹配 + if (FindFlags & (int32)ESerialDevicesFindFlags::ESDFF_PartialCharacterMatching) + { + //判断是否使用部分匹配并判断是否区分大小写 + if (it.Description.Contains(DeviceName, (FindFlags & (int32)ESerialDevicesFindFlags::ESDFF_CaseSensitive) ? ESearchCase::CaseSensitive : ESearchCase::IgnoreCase)) + { + ReturnValue.Add(it); + } + } + else + { + if (it.Description.Equals(DeviceName, (FindFlags & (int32)ESerialDevicesFindFlags::ESDFF_CaseSensitive) ? ESearchCase::CaseSensitive : ESearchCase::IgnoreCase)) + { + ReturnValue.Add(it); + } + } + + + } + } + + return ReturnValue; +} + +USerialCom* USerialCom::FindAndOpenSerialPortByDeviceName(FString DeviceName, bool& bOpened, int32& FindComPort, int32 FindFlags /*= 0x01*/, int32 BaudRate /*= 9600*/) +{ + if (FindSerialPortDevicePortNumber(DeviceName, FindComPort, FindFlags)) + { + USerialCom* Serial = OpenComPort(bOpened, FindComPort, BaudRate); + return Serial; + } + return nullptr; +} + USerialCom* USerialCom::OpenComPort(bool& bOpened, int32 Port, int32 BaudRate) { USerialCom* Serial = NewObject(); diff --git a/SerialCOM/Source/SERIALCOM/Public/SerialCom.h b/SerialCOM/Source/SERIALCOM/Public/SerialCom.h index c870cc2..6ba8e65 100644 --- a/SerialCOM/Source/SERIALCOM/Public/SerialCom.h +++ b/SerialCOM/Source/SERIALCOM/Public/SerialCom.h @@ -26,6 +26,80 @@ enum class ELineEnd : uint8 nr UMETA(DisplayName = "\n\r") }; +/** + * @enum ESerialDevicesFindFlags + * @brief Flags to control the behavior of serial device searching. + * + * This enumeration contains flags that can be used to control the behavior of functions that search for serial devices. + * Each flag represents a different search option or constraint. + */ +UENUM(BlueprintType, Category = "Communication Serial Find Flags", meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) +enum class ESerialDevicesFindFlags : uint8 +{ + /** + * @brief Invalid flag. + * + * This flag cannot be used. It's likely a placeholder or reserved for future usage. + */ + ESDFF_Error = 0x00 UMETA(DisplayName = "Error"), + /** + * @brief Partial character match. + * + * If this flag is set, the search function will match devices whose names contain the search string as a substring. + * If this flag is not set, the search function will perform a full character match, only matching devices whose names are exactly the same as the search string. + */ + ESDFF_PartialCharacterMatching = 0x01 UMETA(DisplayName = "PartialCharacterMatching"), + /** + * @brief Case sensitive. + * + * If this flag is set, the search function will perform a case-sensitive search, meaning that "Device1" and "device1" would be considered different names. + * If this flag is not set, the search function will perform a case-insensitive search. + */ + ESDFF_CaseSensitive = 0x02 UMETA(DisplayName = "CaseSensitive"), + /** + * @brief Regular expression match. + * + * If this flag is set, the search string will be treated as a regular expression and the search function will match devices whose names match the regular expression. + * This flag can only be used alone, not in combination with other flags. + */ + ESDFF_RegularExpressionMatching = 0x04 UMETA(DisplayName = "RegularExpressionMatching(Multiple Choice)"), + +}; + +ENUM_CLASS_FLAGS(ESerialDevicesFindFlags); + +USTRUCT(BlueprintType) +struct FSerialPortInfo +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite) + int32 Port = -1; + UPROPERTY(BlueprintReadWrite) + FString PortName; + UPROPERTY(BlueprintReadWrite) + FString Description; + + //define constructs + FSerialPortInfo() = default; + FSerialPortInfo(int32 InPort, FString InPortName, FString InDescription) + { + Port = InPort; + PortName = InPortName; + Description = InDescription; + } + FSerialPortInfo(const FSerialPortInfo& Other) = default; + FSerialPortInfo& operator=(const FSerialPortInfo& Other) + { + this->Port = Other.Port; + this->PortName = Other.PortName; + this->Description = Other.Description; + return *this; + } + FSerialPortInfo(FSerialPortInfo&& Other) = default; + +}; + UCLASS(BlueprintType, Category = "Communication Serial", meta = (Keywords = "com arduino serial arduino duino")) class SERIALCOM_API USerialCom : public UObject { @@ -54,13 +128,63 @@ class SERIALCOM_API USerialCom : public UObject UFUNCTION(BlueprintCallable, meta = (DisplayName = "Open Serial Port With Flow Control"), Category = "Communication Serial", meta = (Keywords = "communication com SERIALCOM duino arduino serial port start open serial with flow control")) static USerialCom* OpenComPortWithFlowControl(bool &bOpened, int32 Port = 1, int32 BaudRate = 9600, bool DTR = true, bool RTS = true); - /** - * Utility function to convert 4 bytes into an Integer. If the input array's length is not 4, returns 0. - * - * @param Bytes A byte array with 4 values representing the integer in little-endian format. - * @return The final integer value or 0 for an invalid array. - */ - + /* + * This function scans the system for all serial port devices and collects their names and port numbers. + * It returns an array of FSerialPortInfo objects, each containing the name and port number of a serial port device. + * + * @return All serial port devices and port numbers structure + */ + UFUNCTION(BlueprintPure, Category = "Communication Serial") + static TArray GetAllSerialPortDevicesAndPortNumbers(); + /** + * @brief Find the port number of a serial port device. + * + * This function attempts to find the port number of a specific serial port device based on its name. + * If the device is found, the function returns true and the port number is stored in the reference parameter 'FindComPort'. + * If the device is not found, the function returns false. + * + * @param DeviceName - The name of the serial port device to find. + * @param FindComPort - A reference parameter where the found port number will be stored. + * @param FindFlags - Flags to control the search behavior . + * + * @return bool - Returns true if the device is found, false otherwise. + */ + UFUNCTION(BlueprintPure, Category = "Communication Serial",meta = (Keywords = "communication com SERIALCOM duino arduino serial port serial FindPor FindCom")) + static bool FindSerialPortDevicePortNumber(FString DeviceName, int32& FindComPort, UPARAM(meta = (Bitmask, BitmaskEnum = ESerialDevicesFindFlags)) int32 FindFlags = 0x01); + + /** + * @brief Find the port numbers and information of a serial port device. + * + * This function attempts to find the port numbers and related information of a specific serial port device based on its name. + * If the device is found, the function returns an array of FSerialPortInfo structure, each containing the port number and information of a matching device. + * + * @param DeviceName - The name of the serial port device to find. + * + * @param FindFlags - Flags to control the search behavior (the specific meaning of the flags depends on the implementation). + * + * @return TArray - Returns an array of FSerialPortInfo structure if the device is found. Each FSerialPortInfo object contains the port number and information of a matching device. + */ + UFUNCTION(BlueprintPure, Category = "Communication Serial", meta = (Keywords = "communication com SERIALCOM duino arduino serial port serial FindPor FindCom")) + static TArray FindAllSerialPortDevicePortInfo(FString DeviceName, UPARAM(meta = (Bitmask, BitmaskEnum = ESerialDevicesFindFlags)) int32 FindFlags = 0x01); + + /** + * @brief Find and open a serial port by device name. + * + * This function attempts to find a serial port device by its name and open it. If the device is found and the port is not occupied, + * the function returns a valid USerialCom object and sets the 'bOpened' reference parameter to true. If the port is occupied, + * the function still returns a valid USerialCom object, but sets 'bOpened' to false. If the device is not found, the function returns a null pointer. + * + * @param DeviceName - The name of the serial port device to find and open. + * @param FindFlags - Flags to control the search behavior (the specific meaning of the flags depends on the implementation). + * @param bOpened - A reference parameter that indicates whether the port was successfully opened (true) or not (false). + * @param FindComPort - A reference parameter where the found port number will be stored. + * @param Port - The port number to open (default is 1). + * @param BaudRate - The baud rate to use when opening the port (default is 9600). + * + * @return USerialCom* - Returns a valid USerialCom object if the device is found, null otherwise. Even if the port is occupied, a valid USerialCom object is returned. + */ + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Open Serial Port"), Category = "Communication Serial", meta = (Keywords = "communication com SERIALCOM duino arduino serial port start open serial")) + static USerialCom* FindAndOpenSerialPortByDeviceName(FString DeviceName, bool& bOpened, int32& FindComPort, UPARAM(meta = (Bitmask, BitmaskEnum = ESerialDevicesFindFlags)) int32 FindFlags = 0x01, int32 BaudRate = 9600);