Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 172 additions & 1 deletion SerialCOM/Source/SERIALCOM/Private/SerialCom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

#include "SerialCom.h"

#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/MinWindows.h"
#include "Windows/HideWindowsPlatformTypes.h"

#include <Setupapi.h> //SetupDiGetClassDevs Setup*
#include <initguid.h> //GUID
#include <vector>
#include <string>
#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

Expand All @@ -16,12 +24,175 @@ USerialCom* USerialCom::OpenComPortWithFlowControl(bool& bOpened, int32 Port, in
return Serial;
}

TArray<FSerialPortInfo> USerialCom::GetAllSerialPortDevicesAndPortNumbers()
{
TArray<FSerialPortInfo> 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<FSerialPortInfo>();
}

bool USerialCom::FindSerialPortDevicePortNumber(FString DeviceName, int32& FindComPort, int32 FindFlags)
{
TArray<FSerialPortInfo> LocalFindSerialPort = FindAllSerialPortDevicePortInfo(DeviceName, FindFlags);
if (LocalFindSerialPort.Num() > 0)
{
FindComPort = LocalFindSerialPort[0].Port;
return true;
}
return false;
}


//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////


TArray<FSerialPortInfo> USerialCom::FindAllSerialPortDevicePortInfo(FString DeviceName, int32 FindFlags)
{
TArray<FSerialPortInfo> 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<USerialCom>();
Expand Down
138 changes: 131 additions & 7 deletions SerialCOM/Source/SERIALCOM/Public/SerialCom.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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<FSerialPortInfo> 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<FSerialPortInfo> - 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<FSerialPortInfo> 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);



Expand Down