Add Status subsystem

This commit is contained in:
Lucas Peter 2024-09-23 15:48:29 +02:00
parent 804e5fe911
commit 5f126e2b55
No known key found for this signature in database
2 changed files with 225 additions and 20 deletions

View file

@ -3,6 +3,9 @@
#include "SkyPortalSubsystem.h"
#include "Engine/Engine.h"
#include "HAL/RunnableThread.h"
#include "Misc/ScopeLock.h"
DEFINE_LOG_CATEGORY(LogHIDApi);
DEFINE_LOG_CATEGORY(LogSkyportalIO);
@ -10,13 +13,28 @@ void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
// Custom initialization logic
// Start the status checker thread
StatusChecker = new FPortalStatusChecker(this, 0.1f); // Check every 5 seconds
StatusCheckerThread = FRunnableThread::Create(StatusChecker, TEXT("PortalStatusCheckerThread"), 0, TPri_AboveNormal);
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized"));
}
void USkyPortalSubsystem::Deinitialize()
{
// Stop the thread when the subsystem is deinitialized
if (StatusChecker)
{
StatusChecker->Stop();
StatusCheckerThread->WaitForCompletion();
delete StatusCheckerThread;
delete StatusChecker;
StatusChecker = nullptr;
StatusCheckerThread = nullptr;
}
// Disconnect portal
if (PortalDevice) {
hid_close(PortalDevice);
@ -263,6 +281,51 @@ void USkyPortalSubsystem::Sleep(int sleepMs) {
FPlatformProcess::Sleep(sleepMs * 0.0001);
}
void USkyPortalSubsystem::CheckComplexResponse() {
if (!PortalDevice) {
return;
}
RWBlock req, res;
memset(req.buf, 0, rw_buf_size);
req.buf[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[1]);
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:
break;
default:
break;
}
}
/* Verify the command response, when only a character is sended by the portal.
*
*TODO: Refacto this function to handle better the response/output from the portal
@ -276,7 +339,7 @@ bool USkyPortalSubsystem::CheckResponse(RWBlock* res, char expect)
int b = hid_read_timeout(PortalDevice, res->buf, rw_buf_size, TIMEOUT);
if (b < 0) {
UE_LOG(LogSkyportalIO, Error, TEXT("Unable to read Skylander from Portal.\n"));
UE_LOG(LogSkyportalIO, Error, TEXT("Error.\n %s"), hid_error(PortalDevice));
return false;
}
@ -348,6 +411,35 @@ bool USkyPortalSubsystem::WriteBlock(unsigned int block, unsigned char data[0x10
}
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::ReadBlock(unsigned int block, unsigned char data[0x10], int skylander) {
RWBlock req, res; //request and response buffers
unsigned char followup;
@ -411,7 +503,7 @@ bool USkyPortalSubsystem::ConnectPortal()
Sleep(500);
ChangePortalColor(FLinearColor(0xC8, 0xC8, 0xC8));
UE_LOG(LogSkyportalIO, Log, TEXT("Portal Status: "), PortalStatus());
UE_LOG(LogSkyportalIO, Log, TEXT("Portal connected: "));
}
return bPortalConnected;
}
@ -433,13 +525,56 @@ void USkyPortalSubsystem::SendPortalSound(USoundWave* Sound)
unsigned char USkyPortalSubsystem::PortalStatus()
FPortalStatusChecker::FPortalStatusChecker(USkyPortalSubsystem* InSubsystem, float InCheckInterval)
: SkyPortalSubsystem(InSubsystem), CheckInterval(InCheckInterval), bShouldRun(true)
{
RWBlock req, res;
memset(req.buf, 0, rw_buf_size);
req.buf[1] = 'S';
do { Write(&req); } while (CheckResponse(&res, 'S'));
return res.buf[1];
}
bool FPortalStatusChecker::Init()
{
// Initialization logic, if necessary (e.g., logging)
return true;
}
uint32 FPortalStatusChecker::Run()
{
// Main loop of the thread, runs until Stop() is called
while (bShouldRun)
{
// Check the portal status
CheckPortalStatus();
// Sleep for the specified interval
FPlatformProcess::Sleep(CheckInterval);
}
return 0; // Exit code for the thread
}
void FPortalStatusChecker::Stop()
{
// Signal the thread to stop running
bShouldRun = false;
}
void FPortalStatusChecker::Exit()
{
// Cleanup after the thread has stopped
}
void FPortalStatusChecker::CheckPortalStatus()
{
// Ensure the subsystem is valid
if (SkyPortalSubsystem && SkyPortalSubsystem->bPortalConnected)
{
UE_LOG(LogSkyportalIO, Verbose, TEXT("Check portal"));
// Call the subsystem function to get portal status
SkyPortalSubsystem->CheckComplexResponse();
// Do something with the status (log, notify, etc.)
UE_LOG(LogSkyportalIO, Verbose, TEXT("Portal Status:"));
}
}

View file

@ -3,6 +3,7 @@
#include "CoreMinimal.h"
#include "Subsystems/EngineSubsystem.h"
#include "hidapi.h"
#include "HAL/Runnable.h"
#include "SkyPortalSubsystem.generated.h"
@ -29,13 +30,44 @@ enum EPortalCommand {
S UMETA(DisplayName = "Status")
};
UENUM(BlueprintType)
enum class EFigureStatus : uint8
{
NOT_PRESENT = 0b00 UMETA(DisplayName = "Not Present"),
PRESENT = 0b01 UMETA(DisplayName = "Present"),
ADDED = 0b11 UMETA(DisplayName = "Added"),
REMOVED = 0b10 UMETA(DisplayName = "Removed")
};
USTRUCT(BlueprintType)
struct FPortalStatusResponse
{
GENERATED_BODY()
// Array of statuses
UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure")
TArray<EFigureStatus> 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;
};
/* Macro constants Definitions */
#define rw_buf_size 0x21
#define TIMEOUT 30000
#define DEBUG true
typedef struct {
unsigned char buf[rw_buf_size]; int BytesTransferred;
} RWBlock;
DECLARE_LOG_CATEGORY_EXTERN(LogHIDApi, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
@ -46,6 +78,38 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderRemovedDelegate, int32,
#pragma endregion
class FPortalStatusChecker : public FRunnable {
public:
// Constructor: pass the subsystem and desired check interval (in seconds)
FPortalStatusChecker(USkyPortalSubsystem* InSubsystem, float InCheckInterval);
// FRunnable interface
virtual bool Init() override;
virtual uint32 Run() override;
virtual void Stop() override;
virtual void Exit() override;
private:
// Pointer to the subsystem that contains PortalStatus()
USkyPortalSubsystem* SkyPortalSubsystem;
// Time interval (in seconds) for status checking
float CheckInterval;
// Thread control variables
FThreadSafeBool bShouldRun;
// Helper function to check portal status
void CheckPortalStatus();
};
/* Handle all the portal I/O
*
*
@ -107,28 +171,31 @@ public:
FOnSkylanderRemovedDelegate OnSkylanderRemoved;
EPortalCommand GetPortalCommandFromChar(unsigned char Char);
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
bool bPortalConnected = false;
FString HidError;
unsigned char PortalStatus();
bool ReadBlock(unsigned int block, unsigned char data[0x10], int skylander);
bool WriteBlock(unsigned int, unsigned char[0x10], int);
bool CheckResponse(RWBlock*, char);
void CheckComplexResponse();
private:
typedef struct {
unsigned char buf[rw_buf_size]; int BytesTransferred;
} RWBlock;
static void Sleep(int sleepMs);
void ActivatePortal(int active);
void RestartPortal();
@ -136,12 +203,14 @@ private:
// Block/byte related data write/read functions
bool OpenPortalHandle();
bool ReadBlock(unsigned int block, unsigned char data[0x10], int skylander);
bool WriteBlock(unsigned int, unsigned char[0x10], int);
bool CheckResponse(RWBlock*, char);
void Write(RWBlock*);
// Pointer to the status checker thread
FPortalStatusChecker* StatusChecker;
FRunnableThread* StatusCheckerThread;
protected:
//Constants
@ -155,3 +224,4 @@ protected:
//const int ProductId = 336;
};