From 6ffaeb801a4781ce02b082036970b8a5975d2c9a Mon Sep 17 00:00:00 2001 From: LUCASTUCIOUS Date: Thu, 26 Sep 2024 00:19:16 +0200 Subject: [PATCH] Run, but status array regression --- Source/SkyPortal/Private/SkyPortalIO.cpp | 78 ++++++- Source/SkyPortal/Private/SkyPortalRunner.cpp | 49 ++++- .../SkyPortal/Private/SkyPortalSubsystem.cpp | 194 ++---------------- Source/SkyPortal/Public/SkyPortalIO.h | 73 ++++++- Source/SkyPortal/Public/SkyPortalRunner.h | 51 +---- Source/SkyPortal/Public/SkyPortalSubsystem.h | 53 ++--- 6 files changed, 236 insertions(+), 262 deletions(-) diff --git a/Source/SkyPortal/Private/SkyPortalIO.cpp b/Source/SkyPortal/Private/SkyPortalIO.cpp index c198fe9..d77711a 100644 --- a/Source/SkyPortal/Private/SkyPortalIO.cpp +++ b/Source/SkyPortal/Private/SkyPortalIO.cpp @@ -181,4 +181,80 @@ bool USkyPortalIO::IsFalsePositive() const return (dif <= 2 || dif >= 254); } -*/ \ No newline at end of file +*/ + +EPortalCommand GetPortalCommandFromChar(unsigned char Char) +{ + switch (Char) + { + case 'A': + return EPortalCommand::A; + case 'C': + return EPortalCommand::C; + case 'J': + return EPortalCommand::J; + case 'L': + return EPortalCommand::L; + case 'M': + return EPortalCommand::M; + case 'Q': + return EPortalCommand::Q; + case 'R': + return EPortalCommand::R; + case 'S': + return EPortalCommand::S; + default: + // Handle the case when the character doesn't match any enum + // Return a default or invalid value, or handle the error + UE_LOG(LogSkyportalIO, Warning, TEXT("Invalid character for Portal Command: %c"), TCHAR(Char)); + return EPortalCommand::S; // 'S' for Status as a default + } + +} + +FPortalStatusData ParsePortalStatus(const uint8* StatusResponse) +{ + FPortalStatusData result; + result.Counter = StatusResponse[5]; + result.bIsReady = (StatusResponse[6] == 0x01); + + // Parse the figure status array (little-endian 32-bit integer) + uint32 FigureStatusArray = 0; + // Reading the 32-bit integer (character status array) from the buffer + FigureStatusArray |= StatusResponse[1]; // 1st byte + FigureStatusArray |= (StatusResponse[2] << 8); // 2nd byte + FigureStatusArray |= (StatusResponse[3] << 16); // 3rd byte + FigureStatusArray |= (StatusResponse[4] << 24); // 4th byte + + bool bChangeBitsSet = false; + + TStaticArray tempArray; + // For each of the 16 entries, extract the 2-bit status and map it to EFigureStatus + for (int32 i = 0; i < 16; ++i) + { + uint8 StatusBits = (FigureStatusArray >> (i * 2)) & 0b11; // Extract 2 bits + EFigureStatus FigureStatus; + + switch (StatusBits) + { + case 0b00: + FigureStatus = EFigureStatus::NOT_PRESENT; + case 0b01: + FigureStatus = EFigureStatus::PRESENT; + case 0b11: + FigureStatus = EFigureStatus::ADDED; + case 0b10: + FigureStatus = EFigureStatus::REMOVED; + default: + FigureStatus = EFigureStatus::NOT_PRESENT; // Default case + } + + // Add to the array of figure statuses + //PortalStatusData.StatusArray.Insert(FigureStatus, i); + tempArray[i] = FigureStatus; + } + result.StatusArray.SetNum(0); + result.StatusArray.Append(tempArray); + + return result; + } diff --git a/Source/SkyPortal/Private/SkyPortalRunner.cpp b/Source/SkyPortal/Private/SkyPortalRunner.cpp index 355ae06..75f2871 100644 --- a/Source/SkyPortal/Private/SkyPortalRunner.cpp +++ b/Source/SkyPortal/Private/SkyPortalRunner.cpp @@ -1,4 +1,5 @@ #include "SkyPortalRunner.h" +#include "SkyPortalFigure.h" #include "SkyPortalSubsystem.h" FPortalStatusChecker::FPortalStatusChecker(UEngineSubsystem* InSubsystem, float InCheckInterval) @@ -43,13 +44,57 @@ void FPortalStatusChecker::CheckPortalStatus() // Ensure the subsystem is valid if (SkyPortalSubsystemRef && Cast(SkyPortalSubsystemRef)) { - USkyPortalIO* PortalHandleRef = Cast(SkyPortalSubsystemRef)->PortalHandle; + USkyPortalSubsystem* subref = Cast(SkyPortalSubsystemRef); + USkyPortalIO* PortalHandleRef = subref->PortalHandle; UE_LOG(LogSkyportalIO, Verbose, TEXT("Check portal")); if (PortalHandleRef && PortalHandleRef->bPortalReady) { // Call the subsystem function to get portal status + uint8* output = PortalHandleRef->Read(); + EPortalCommand CommandResponse = GetPortalCommandFromChar(output[0]); + switch (CommandResponse) + { + case S: + CurrentStatusData = ParsePortalStatus(output); - Cast(SkyPortalSubsystemRef); + //Send delegate when new informations are received + if (OldStatusData != CurrentStatusData) { + + + for (int i = 0; i < CurrentStatusData.StatusArray.Num(); i++) { + if (CurrentStatusData.StatusArray[i] != OldStatusData.StatusArray[i]) { + if ( + //!FalsePositive() //filter conflicting infos + true) { + //FigureDataBlock FigureData; + switch (CurrentStatusData.StatusArray[i]) + { + case EFigureStatus::NOT_PRESENT: + subref->OnSkylanderRemoved.Broadcast(00, i); + break; + case EFigureStatus::PRESENT: + //FigureData = ReadFigureBlocks(i); + //subref->OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i); + break; + case EFigureStatus::ADDED: + //FigureData = ReadFigureBlocks(i); + //subref->OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i); + case EFigureStatus::REMOVED: + subref->OnSkylanderRemoved.Broadcast(00, i); + } + } + } + + + + OldStatusData = CurrentStatusData; + subref->StatusData = CurrentStatusData; + } + } + break; + default: + return; + } // Do something with the status (log, notify, etc.) UE_LOG(LogSkyportalIO, Verbose, TEXT("Portal Status:")); diff --git a/Source/SkyPortal/Private/SkyPortalSubsystem.cpp b/Source/SkyPortal/Private/SkyPortalSubsystem.cpp index 6c5f525..6f4e162 100644 --- a/Source/SkyPortal/Private/SkyPortalSubsystem.cpp +++ b/Source/SkyPortal/Private/SkyPortalSubsystem.cpp @@ -13,7 +13,7 @@ void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection) Super::Initialize(Collection); // Start the status checker thread - StatusChecker = new FPortalStatusChecker(this, 0.1f); // Check every 5 seconds + StatusChecker = new FPortalStatusChecker(this, 0.1f); // Check every 50 milliseconds StatusCheckerThread = FRunnableThread::Create(StatusChecker, TEXT("PortalStatusCheckerThread"), 0, TPri_AboveNormal); UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized")); @@ -80,6 +80,24 @@ void USkyPortalSubsystem::PortalReady() } +FPortalStatusData USkyPortalSubsystem::PortalStatus() { + FWriteBlock command; + + memset(command.data, 0, write_buf_size); //maybe not needed here + command.data[1] = 'S'; + uint8* output; + + do { + PortalHandle->Write(&command); + output = PortalHandle->Read(); + } while (output[0] != 'S'); + + + + return ParsePortalStatus(output); +} + + #pragma region Color functions @@ -164,7 +182,7 @@ void USkyPortalSubsystem::ChangePortalColorSide(const FLinearColor& Color, const -FPortalStatusData USkyPortalSubsystem::ParsePortalStatus(const FWriteBlock& ResponseBlock) +FPortalStatusData USkyPortalSubsystem::OLDParsePortalStatus(const FWriteBlock& ResponseBlock) { FPortalStatusData PortalStatusData; @@ -221,167 +239,6 @@ void USkyPortalSubsystem::Sleep(int sleepMs) { FPlatformProcess::Sleep(sleepMs * 0.0001); } -/* -void USkyPortalSubsystem::CheckComplexResponse() { - - if (!PortalDevice) { - return; - } - - RWBlock req, res; - - memset(command.data, 0, rw_buf_size); - command.data[1] = 'S'; - Write(&req); - - int BuffResponse = hid_read_timeout(PortalDevice, res.buf, rw_buf_size, TIMEOUT); - if (BuffResponse < 0) { - UE_LOG(LogSkyportalIO, Error, TEXT("Error.\n %s"), hid_error(PortalDevice)); - return; - } - EPortalCommand CommandResponse = GetPortalCommandFromChar(res.buf[0]); - switch (CommandResponse) - { - case A: - break; - case C: - break; - case J: - break; - case L: - break; - case M: - break; - case Q: - break; - case R: - break; - case S: - CurrentStatusData = ParsePortalStatus(res); - - //Send delegate when new informations are received - if (OldStatusData != CurrentStatusData) { - - for (int i = 0; i < CurrentStatusData.StatusArray.Num(); i++) { - if (CurrentStatusData.StatusArray[i] != OldStatusData.StatusArray[i]) { - if ( - //!FalsePositive() //filter conflicting infos - true) { - FigureDataBlock FigureData; - switch (CurrentStatusData.StatusArray[i]) - { - case EFigureStatus::NOT_PRESENT: - OnSkylanderRemoved.Broadcast(00, i); - break; - case EFigureStatus::PRESENT: - FigureData = ReadFigureBlocks(i); - OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i); - break; - case EFigureStatus::ADDED: - FigureData = ReadFigureBlocks(i); - OnSkylanderAdded.Broadcast(GetFigureID(FigureData), i); - case EFigureStatus::REMOVED: - OnSkylanderRemoved.Broadcast(00, i); - } - } - } - } - - - OldStatusData = CurrentStatusData; - } - - - break; - default: - break; - } - -} -*/ - - - - - - - - - -/* Verify the command response, when only a character is sended by the portal. -* - - -bool USkyPortalSubsystem::CheckResponse(RWBlock* res, char expect) -{ - if (!PortalDevice) { - return false; - } - - int b = hid_read_timeout(PortalDevice, res->buf, rw_buf_size, TIMEOUT); - - if (b < 0) { - UE_LOG(LogSkyportalIO, Error, TEXT("Error.\n %s"), hid_error(PortalDevice)); - return false; - } - - res->BytesTransferred = b; - - - - // found wireless USB but portal is not connected -if (res->buf[0] == 'Z') -{ - UE_LOG(LogSkyportalIO, Error, TEXT("found wireless USB but portal is not connected")); - return false; -} - -// Status says no skylander on portal -if (res->buf[0] == 'Q' && res->buf[1] == 0) { - UE_LOG(LogSkyportalIO, Warning, TEXT("Status says no skylander on portal")); -} - -if (res->buf[0] == 'R' && res->buf[1] == 0) { - UE_LOG(LogSkyportalIO, Warning, TEXT("Status says no skylander on portal")); -} - -return (res->buf[0] != expect); -} -*/ - - - - -EPortalCommand USkyPortalSubsystem::GetPortalCommandFromChar(unsigned char Char) -{ - switch (Char) - { - case 'A': - return EPortalCommand::A; - case 'C': - return EPortalCommand::C; - case 'J': - return EPortalCommand::J; - case 'L': - return EPortalCommand::L; - case 'M': - return EPortalCommand::M; - case 'Q': - return EPortalCommand::Q; - case 'R': - return EPortalCommand::R; - case 'S': - return EPortalCommand::S; - default: - // Handle the case when the character doesn't match any enum - // Return a default or invalid value, or handle the error - UE_LOG(LogSkyportalIO, Warning, TEXT("Invalid character for Portal Command: %c"), TCHAR(Char)); - return EPortalCommand::S; // 'S' for Status as a default - } - -} - - bool USkyPortalSubsystem::PortalConnect() { @@ -407,18 +264,11 @@ bool USkyPortalSubsystem::IsPortalReady() return false; } -void USkyPortalSubsystem::SendPortalCommand(EPortalCommand Command) +SKYPORTAL_API void USkyPortalSubsystem::PortalMusic(const USoundWave* Sound) { - + return ; } -void USkyPortalSubsystem::SendPortalSound(USoundWave* Sound) -{ - -} - - - /* FigureDataBlock USkyPortalSubsystem::ReadFigureBlocks(uint8 FigureIndex) diff --git a/Source/SkyPortal/Public/SkyPortalIO.h b/Source/SkyPortal/Public/SkyPortalIO.h index 5455148..54a31c7 100644 --- a/Source/SkyPortal/Public/SkyPortalIO.h +++ b/Source/SkyPortal/Public/SkyPortalIO.h @@ -4,9 +4,11 @@ */ #include "CoreMinimal.h" #include "hidapi.h" - +#include "SkyPortalFigure.h" #include "SkyPortalIO.generated.h" +#pragma region Definitions + DECLARE_LOG_CATEGORY_EXTERN(LogHIDApi, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All); @@ -17,9 +19,71 @@ constexpr auto TIMEOUT = 30000; //milliseconds const int VendorIds[4] = { 0x12ba, 0x54c, 0x1430, 0x1430 }; const int ProductIds[4] = { 0x150, 0x967, 0x1f17 }; -/* WriteBlock -* -* Contain all the data that pass inside hidapi write + the number of bytes in case of successful write +UENUM(BlueprintType) +enum EPortalCommand { + A UMETA(DisplayName = "Activate"), + C UMETA(DisplayName = "Color"), + J UMETA(DisplayName = "Advanced color"), + L UMETA(DisplayName = "Trap color"), + M UMETA(DisplayName = "Music"), + Q UMETA(DisplayName = "Query"), + R UMETA(DisplayName = "Ready"), + S UMETA(DisplayName = "Status") +}; + +USTRUCT(BlueprintType) +struct FPortalStatusData +{ + GENERATED_BODY() + + // Array of statuses + UPROPERTY(BlueprintReadOnly, EditFixedSize, Category = "SkyPortal|Figure", meta = (EditFixedOrder)) + TArray StatusArray; + + // timestamp. + // only one byte long. This means that after the value 0xFF, it overflows back to 0x00. + // Since these are so far apart, it can be assumed that 0x00 is newer than anything in the range 0xF0 - 0xFF. + UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") + uint8 Counter; + + // Should always be true + UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") + bool bIsReady; + + explicit FPortalStatusData(uint8 ArraySize = 16, EFigureStatus DefaultStatus = EFigureStatus::NOT_PRESENT) : + Counter(0), + bIsReady(true) + { + // Initialisation du tableau StatusArray avec 16 éléments par défaut + StatusArray.Init(DefaultStatus, ArraySize); + } + + // Overload the == operator + // Different only between + bool operator==(const FPortalStatusData& Other) const + { + if (bIsReady == Other.bIsReady && Counter == Other.Counter) { + if (StatusArray == Other.StatusArray) { + return true; + } + } + return false; + } + + // Overload the != operator + bool operator!=(const FPortalStatusData& Other) const + { + return !(*this == Other); + } +}; +#pragma endregion + + +FPortalStatusData ParsePortalStatus(const uint8* StatusResponse); +EPortalCommand GetPortalCommandFromChar(unsigned char Char); + +/* Contain all the data that pass inside hidapi write + the number of bytes in case of successful write +* * @param data all the block data, should be 0x21 sized * @param BytesTransferred In case of successful write, print the number of byte transfered. Could be used for verifications */ @@ -69,6 +133,7 @@ public: /* Close connection to Portal*/ void Close(); + private: /*TODO: Should not be here diff --git a/Source/SkyPortal/Public/SkyPortalRunner.h b/Source/SkyPortal/Public/SkyPortalRunner.h index 2c6e9da..16f6832 100644 --- a/Source/SkyPortal/Public/SkyPortalRunner.h +++ b/Source/SkyPortal/Public/SkyPortalRunner.h @@ -1,55 +1,8 @@ #pragma once -#include "Core.h" #include "HAL/Runnable.h" -#include "SkyPortalFigure.h" -#include "SkyPortalRunner.generated.h" -USTRUCT(BlueprintType) -struct FPortalStatusData -{ - GENERATED_BODY() - - // Array of statuses - UPROPERTY(BlueprintReadOnly, EditFixedSize, Category = "SkyPortal|Figure", meta = (EditFixedOrder)) - TArray StatusArray; - - // timestamp. - // only one byte long. This means that after the value 0xFF, it overflows back to 0x00. - // Since these are so far apart, it can be assumed that 0x00 is newer than anything in the range 0xF0 - 0xFF. - UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") - uint8 Counter; - - // Should always be true - UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure") - bool bIsReady; - - explicit FPortalStatusData(uint8 ArraySize = 16, EFigureStatus DefaultStatus = EFigureStatus::NOT_PRESENT) : - Counter(0), // Utilisation correcte de l'initialisation directe - bIsReady(true) // Utilisation correcte de l'initialisation directe - { - // Initialisation du tableau StatusArray avec 16 éléments par défaut - StatusArray.Init(DefaultStatus, ArraySize); - } - - // Overload the == operator - bool operator==(const FPortalStatusData& Other) const - { - if (bIsReady == Other.bIsReady && Counter == Other.Counter) { - if (StatusArray == Other.StatusArray) { - return true; - } - } - return false; - } - - // Overload the != operator - bool operator!=(const FPortalStatusData& Other) const - { - return !(*this == Other); - } -}; class FPortalStatusChecker : public FRunnable { @@ -66,7 +19,11 @@ public: virtual void Stop() override; virtual void Exit() override; + UPROPERTY(BlueprintReadOnly) + FPortalStatusData CurrentStatusData; + UPROPERTY(BlueprintReadOnly) + FPortalStatusData OldStatusData; private: // Pointer to the subsystem that contains PortalStatus() diff --git a/Source/SkyPortal/Public/SkyPortalSubsystem.h b/Source/SkyPortal/Public/SkyPortalSubsystem.h index 022bd8c..95baa5c 100644 --- a/Source/SkyPortal/Public/SkyPortalSubsystem.h +++ b/Source/SkyPortal/Public/SkyPortalSubsystem.h @@ -17,18 +17,6 @@ enum EPortalSide { TRAP UMETA(DisplayName = "Trap") }; -UENUM(BlueprintType) -enum EPortalCommand { - A UMETA(DisplayName = "Activate"), - C UMETA(DisplayName = "Color"), - J UMETA(DisplayName = "Advanced color"), - L UMETA(DisplayName = "Trap color"), - M UMETA(DisplayName = "Music"), - Q UMETA(DisplayName = "Query"), - R UMETA(DisplayName = "Ready"), - S UMETA(DisplayName = "Status") -}; - @@ -44,11 +32,6 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderRemovedDelegate, int32, - - - - - /* * Handle all the blueprints functions and game logic. * @@ -59,7 +42,6 @@ class USkyPortalSubsystem : public UEngineSubsystem GENERATED_BODY() public: - // Override initialization and deinitialization methods virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; @@ -71,7 +53,7 @@ public: /* The first function to run, before anything else. * It will re-init and prepare the portal for receiving/sending inputs * - * return false if portal is not found + * @return false if portal is not found */ UFUNCTION(BlueprintCallable, CallInEditor, meta = (Category = "SkyPortal")) SKYPORTAL_API bool PortalConnect(); @@ -103,21 +85,20 @@ public: SKYPORTAL_API void ChangePortalColor(const FLinearColor& NextColor = FLinearColor::Green); /*Send a **Status** command, to see if the portal is ready to receive new commands*/ - UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|NOT FUNCTIONING")) + UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal")) SKYPORTAL_API bool IsPortalReady(); UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|NOT FUNCTIONING")) - SKYPORTAL_API void SendPortalCommand(EPortalCommand Command); + SKYPORTAL_API void PortalMusic(const USoundWave* Sound); - UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|NOT FUNCTIONING")) - SKYPORTAL_API void SendPortalSound(USoundWave* Sound); + /**/ + UFUNCTION(BlueprintCallable, CallInEditor, meta = (Category = "SkyPortal|Commands")) + SKYPORTAL_API FPortalStatusData PortalStatus(); + + - - - - /** - * Change the color of the portal, can separate side and even trap ligth + /* Change the color of the portal, can separate side and even trap ligth * @param NextColor New color * @param PortalSide The actors to record * @param BlendTime Blend between current color and NextColor, in milliseconds @@ -125,6 +106,7 @@ public: UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "NextColor", Category = "SkyPortal|Commands|Cosmetic", HideAlphaChannel)) SKYPORTAL_API void ChangePortalColorSide(const FLinearColor& NextColor = FLinearColor::Green, const EPortalSide PortalSide = EPortalSide::BOTH, const float BlendTime = 500.0f); + // Blueprint-assignable event property UPROPERTY(BlueprintAssignable, Category = "SkyPortal|Skylander") @@ -135,16 +117,13 @@ public: FOnSkylanderRemovedDelegate OnSkylanderRemoved; UPROPERTY(BlueprintReadOnly) - FPortalStatusData CurrentStatusData; - - UPROPERTY(BlueprintReadOnly) - FPortalStatusData OldStatusData; + FPortalStatusData StatusData; /* Different for each portal model. * * SSA : * GIANTS : - * SWAP FORCE : 0000547 ? + * SWAP FORCE : 0000547 ? , 515 * TRAP TEAM : * SUPERCHARGERS : * IMAGINATORS : @@ -166,10 +145,14 @@ public: private: - FPortalStatusData ParsePortalStatus(const FWriteBlock& ResponseBlock); + + + // deprecated + FPortalStatusData OLDParsePortalStatus(const FWriteBlock& ResponseBlock); // deprecated EPortalCommand GetPortalCommandFromChar(unsigned char Char); // deprecated + /* Sleep the system * @@ -177,8 +160,6 @@ private: */ static void Sleep(int sleepMs); - - FigureDataBlock ReadFigureBlocks(uint8 FigureIndex); bool FalsePositive() const; // Pointer to the status checker thread