Add Status subsystem
This commit is contained in:
parent
804e5fe911
commit
5f126e2b55
2 changed files with 225 additions and 20 deletions
|
@ -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:"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue