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 "SkyPortalSubsystem.h"
|
||||||
#include "Engine/Engine.h"
|
#include "Engine/Engine.h"
|
||||||
|
|
||||||
|
#include "HAL/RunnableThread.h"
|
||||||
|
#include "Misc/ScopeLock.h"
|
||||||
|
|
||||||
DEFINE_LOG_CATEGORY(LogHIDApi);
|
DEFINE_LOG_CATEGORY(LogHIDApi);
|
||||||
DEFINE_LOG_CATEGORY(LogSkyportalIO);
|
DEFINE_LOG_CATEGORY(LogSkyportalIO);
|
||||||
|
|
||||||
|
@ -10,13 +13,28 @@ void USkyPortalSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
{
|
{
|
||||||
|
|
||||||
Super::Initialize(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"));
|
UE_LOG(LogTemp, Log, TEXT("SkyPortalSubsystem Initialized"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void USkyPortalSubsystem::Deinitialize()
|
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
|
// Disconnect portal
|
||||||
if (PortalDevice) {
|
if (PortalDevice) {
|
||||||
hid_close(PortalDevice);
|
hid_close(PortalDevice);
|
||||||
|
@ -263,6 +281,51 @@ void USkyPortalSubsystem::Sleep(int sleepMs) {
|
||||||
FPlatformProcess::Sleep(sleepMs * 0.0001);
|
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.
|
/* 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
|
*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);
|
int b = hid_read_timeout(PortalDevice, res->buf, rw_buf_size, TIMEOUT);
|
||||||
|
|
||||||
if (b < 0) {
|
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;
|
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) {
|
bool USkyPortalSubsystem::ReadBlock(unsigned int block, unsigned char data[0x10], int skylander) {
|
||||||
RWBlock req, res; //request and response buffers
|
RWBlock req, res; //request and response buffers
|
||||||
unsigned char followup;
|
unsigned char followup;
|
||||||
|
@ -411,7 +503,7 @@ bool USkyPortalSubsystem::ConnectPortal()
|
||||||
Sleep(500);
|
Sleep(500);
|
||||||
ChangePortalColor(FLinearColor(0xC8, 0xC8, 0xC8));
|
ChangePortalColor(FLinearColor(0xC8, 0xC8, 0xC8));
|
||||||
|
|
||||||
UE_LOG(LogSkyportalIO, Log, TEXT("Portal Status: "), PortalStatus());
|
UE_LOG(LogSkyportalIO, Log, TEXT("Portal connected: "));
|
||||||
}
|
}
|
||||||
return bPortalConnected;
|
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);
|
bool FPortalStatusChecker::Init()
|
||||||
req.buf[1] = 'S';
|
{
|
||||||
do { Write(&req); } while (CheckResponse(&res, 'S'));
|
// 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:"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res.buf[1];
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Subsystems/EngineSubsystem.h"
|
#include "Subsystems/EngineSubsystem.h"
|
||||||
#include "hidapi.h"
|
#include "hidapi.h"
|
||||||
|
#include "HAL/Runnable.h"
|
||||||
|
|
||||||
|
|
||||||
#include "SkyPortalSubsystem.generated.h"
|
#include "SkyPortalSubsystem.generated.h"
|
||||||
|
@ -29,13 +30,44 @@ enum EPortalCommand {
|
||||||
S UMETA(DisplayName = "Status")
|
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 */
|
/* Macro constants Definitions */
|
||||||
#define rw_buf_size 0x21
|
#define rw_buf_size 0x21
|
||||||
#define TIMEOUT 30000
|
#define TIMEOUT 30000
|
||||||
#define DEBUG true
|
#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(LogHIDApi, Log, All);
|
||||||
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
|
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
|
||||||
|
|
||||||
|
@ -46,6 +78,38 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderRemovedDelegate, int32,
|
||||||
#pragma endregion
|
#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
|
/* Handle all the portal I/O
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -107,28 +171,31 @@ public:
|
||||||
FOnSkylanderRemovedDelegate OnSkylanderRemoved;
|
FOnSkylanderRemovedDelegate OnSkylanderRemoved;
|
||||||
|
|
||||||
|
|
||||||
|
EPortalCommand GetPortalCommandFromChar(unsigned char Char);
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||||
bool bPortalConnected = false;
|
bool bPortalConnected = false;
|
||||||
|
|
||||||
FString HidError;
|
FString HidError;
|
||||||
|
|
||||||
|
bool ReadBlock(unsigned int block, unsigned char data[0x10], int skylander);
|
||||||
|
bool WriteBlock(unsigned int, unsigned char[0x10], int);
|
||||||
unsigned char PortalStatus();
|
bool CheckResponse(RWBlock*, char);
|
||||||
|
void CheckComplexResponse();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned char buf[rw_buf_size]; int BytesTransferred;
|
|
||||||
} RWBlock;
|
|
||||||
|
|
||||||
|
|
||||||
static void Sleep(int sleepMs);
|
static void Sleep(int sleepMs);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ActivatePortal(int active);
|
void ActivatePortal(int active);
|
||||||
|
|
||||||
void RestartPortal();
|
void RestartPortal();
|
||||||
|
@ -136,12 +203,14 @@ private:
|
||||||
|
|
||||||
// Block/byte related data write/read functions
|
// Block/byte related data write/read functions
|
||||||
bool OpenPortalHandle();
|
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*);
|
void Write(RWBlock*);
|
||||||
|
|
||||||
|
|
||||||
|
// Pointer to the status checker thread
|
||||||
|
FPortalStatusChecker* StatusChecker;
|
||||||
|
FRunnableThread* StatusCheckerThread;
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
//Constants
|
//Constants
|
||||||
|
@ -155,3 +224,4 @@ protected:
|
||||||
//const int ProductId = 336;
|
//const int ProductId = 336;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue