diff --git a/Content/Map/Test_Level.umap b/Content/Map/Test_Level.umap index 26896d9a..ed232bf7 100644 Binary files a/Content/Map/Test_Level.umap and b/Content/Map/Test_Level.umap differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.dll b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.dll new file mode 100644 index 00000000..d98a2a06 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.dll differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.exp b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.exp new file mode 100644 index 00000000..55413084 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.exp differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.exe b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.exe new file mode 100644 index 00000000..95db2bb4 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.exe differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.exp b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.exp new file mode 100644 index 00000000..9eae938e Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.exp differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.lib b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.lib new file mode 100644 index 00000000..aa1e36ba Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.lib differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.pdb b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.pdb new file mode 100644 index 00000000..c013ad7a Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_0.pdb differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.exe b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.exe new file mode 100644 index 00000000..1b8f1be1 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.exe differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.exp b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.exp new file mode 100644 index 00000000..02bd5488 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.exp differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.lib b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.lib new file mode 100644 index 00000000..a7ebf8f5 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.lib differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.pdb b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.pdb new file mode 100644 index 00000000..12920fb5 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_1.pdb differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.exe b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.exe new file mode 100644 index 00000000..4de68af7 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.exe differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.exp b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.exp new file mode 100644 index 00000000..2f7acef3 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.exp differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.lib b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.lib new file mode 100644 index 00000000..4089d0ff Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.lib differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.pdb b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.pdb new file mode 100644 index 00000000..dd79c432 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.patch_2.pdb differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.pdb b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.pdb new file mode 100644 index 00000000..2e685007 Binary files /dev/null and b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor-LuckyDataTransfer.pdb differ diff --git a/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor.modules b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor.modules new file mode 100644 index 00000000..fede3b84 --- /dev/null +++ b/Plugins/LuckyDataTransfer/Binaries/Win64/UnrealEditor.modules @@ -0,0 +1,7 @@ +{ + "BuildId": "37670630", + "Modules": + { + "LuckyDataTransfer": "UnrealEditor-LuckyDataTransfer.dll" + } +} \ No newline at end of file diff --git a/Plugins/LuckyDataTransfer/LuckyDataTransfer.uplugin b/Plugins/LuckyDataTransfer/LuckyDataTransfer.uplugin new file mode 100644 index 00000000..21c22485 --- /dev/null +++ b/Plugins/LuckyDataTransfer/LuckyDataTransfer.uplugin @@ -0,0 +1,23 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "LuckyDataTransfer", + "Description": "Lucky data transfer that transfers data with great luck.", + "Category": "Other", + "CreatedBy": "Noah Bowers", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "LuckyDataTransfer", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/Plugins/LuckyDataTransfer/Resources/Icon128.png b/Plugins/LuckyDataTransfer/Resources/Icon128.png new file mode 100644 index 00000000..1231d4aa Binary files /dev/null and b/Plugins/LuckyDataTransfer/Resources/Icon128.png differ diff --git a/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/LuckyDataTransfer.Build.cs b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/LuckyDataTransfer.Build.cs new file mode 100644 index 00000000..4a5b5036 --- /dev/null +++ b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/LuckyDataTransfer.Build.cs @@ -0,0 +1,57 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class LuckyDataTransfer : ModuleRules +{ + public LuckyDataTransfer(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "WebSockets", + "Json", + "JsonUtilities", + "ImageWriteQueue" + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", "ImageWriteQueue", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Private/LuckyDataTransfer.cpp b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Private/LuckyDataTransfer.cpp new file mode 100644 index 00000000..b95e4a24 --- /dev/null +++ b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Private/LuckyDataTransfer.cpp @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "LuckyDataTransfer.h" + +#define LOCTEXT_NAMESPACE "FLuckyDataTransferModule" + +void FLuckyDataTransferModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FLuckyDataTransferModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FLuckyDataTransferModule, LuckyDataTransfer) \ No newline at end of file diff --git a/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Private/LuckyDataTransferSubsystem.cpp b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Private/LuckyDataTransferSubsystem.cpp new file mode 100644 index 00000000..ed026600 --- /dev/null +++ b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Private/LuckyDataTransferSubsystem.cpp @@ -0,0 +1,154 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "LuckyDataTransferSubsystem.h" +#include "JsonUtilities.h" +#include "JsonObjectConverter.h" +#include "WebSocketsModule.h" +#include "Slate/SceneViewport.h" + +ULuckyDataTransferSubsystem::ULuckyDataTransferSubsystem() +{ +} + +void ULuckyDataTransferSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); +} + +void ULuckyDataTransferSubsystem::Deinitialize() +{ + Super::Deinitialize(); + + if (Socket.IsValid() && Socket->IsConnected()) + { + Socket->Close(); + } +} + +void ULuckyDataTransferSubsystem::Internal_OpenWebsocket(const FString& URL, const FString& Protocol) +{ + const FString NewUrl = URL.IsEmpty() ? TEXT("ws://127.0.0.1:3000/ws") : URL; + const FString NewProtocol = Protocol.IsEmpty() ? TEXT("ws") : Protocol; + + UE_LOG(LogTemp, Warning, TEXT("Opening WebSocket URL: %s"), *NewUrl); + + if (!FModuleManager::Get().IsModuleLoaded("WebSockets")) + { + FModuleManager::Get().LoadModule("WebSockets"); + } + + Socket = FWebSocketsModule::Get().CreateWebSocket(NewUrl); + Socket->Connect(); + + Socket->OnConnected().AddLambda([]() + { + UE_LOG(LogTemp, Warning, TEXT("WebSocket connected")); + }); + + Socket->OnConnectionError().AddLambda([](const FString& Message) + { + UE_LOG(LogTemp, Warning, TEXT("WebSocket connection error")); + }); + + Socket->OnMessage().AddUObject(this, &ULuckyDataTransferSubsystem::Callback_OnMessage); +} + +//Callbacks / Lambas +void ULuckyDataTransferSubsystem::Callback_OnConnected() +{ +} + +void ULuckyDataTransferSubsystem::Callback_OnConnectionError(const FString& Error) +{ +} + +void ULuckyDataTransferSubsystem::Callback_OnMessage(const FString& Message) +{ + if (!Message.IsEmpty()) + { + CommandReady(InterpretData(Message)); + return; + } + + UE_LOG(LogTemp, Warning, TEXT("The message received from the websocket is invalid")); +} + +void ULuckyDataTransferSubsystem::Internal_OnMessageSent(const FString& Message) +{ +} + +void ULuckyDataTransferSubsystem::Callback_OnConnectionClosed() +{ +} + +//Blueprint Exposed Implementation +void ULuckyDataTransferSubsystem::ConnectToWebsocket(const FString& URL, const FString& Protocol) +{ + Internal_OpenWebsocket(URL, Protocol); +} + +void ULuckyDataTransferSubsystem::SendMessage(const FString& Message) +{ + if (Socket.IsValid() && Socket->IsConnected()) + { + Socket->Send(Message); + return; + } + + UE_LOG(LogTemp, Warning, TEXT("WebSocket message failed")); +} + +FPayload ULuckyDataTransferSubsystem::InterpretData(const FString& Message) +{ + FPayload Payload = FPayload(); + + if (!Message.IsEmpty()) + { + TSharedPtr JsonObj; + TSharedRef> Reader = TJsonReaderFactory<>::Create(Message); + + if (FJsonSerializer::Deserialize(Reader, JsonObj) && JsonObj.IsValid()) + { + for (auto& Elem : JsonObj->Values) + { + FCommand Command = FCommand(); + Command.Key = FString(Elem.Key); + Command.Value = Elem.Value->AsNumber(); + + Payload.Commands.Add(Command); + } + } + } + + return Payload; +} + +void ULuckyDataTransferSubsystem::CommandReady(const FPayload& Payload) +{ + if (OnCommandReady.IsBound()) + { + OnCommandReady.Broadcast(Payload); + } +} + +//Blueprint Callable function - Use CreateJsonPayload_Observation in C++ +bool ULuckyDataTransferSubsystem::MakeObservationPayload(const FObservationPayload& Data) +{ + return CreateJsonPayload_Observation(Data); +} + +bool ULuckyDataTransferSubsystem::CreateJsonPayload_Observation(const FObservationPayload& Data) +{ + bool bSuccess = false; + ObservationPayloadString = FString(); + + if (!Data.ObservationState.IsEmpty()) + { + FJsonObjectConverter::UStructToJsonObjectString(Data, ObservationPayloadString); + UE_LOG(LogTemp, Warning, TEXT("Payload observation: %s"), *ObservationPayloadString); + bSuccess = true; + } + + return bSuccess; +} \ No newline at end of file diff --git a/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/LuckyDataTransfer.h b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/LuckyDataTransfer.h new file mode 100644 index 00000000..b9bdb711 --- /dev/null +++ b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/LuckyDataTransfer.h @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FLuckyDataTransferModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/LuckyDataTransferSubsystem.h b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/LuckyDataTransferSubsystem.h new file mode 100644 index 00000000..8cc0e592 --- /dev/null +++ b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/LuckyDataTransferSubsystem.h @@ -0,0 +1,118 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "IWebSocket.h" +//#include "LuckyWriteThread.h" +#include "ObservationData.h" +#include "Subsystems/WorldSubsystem.h" +#include "LuckyDataTransferSubsystem.generated.h" + +/** + * + */ +USTRUCT(BlueprintType) +struct FCommand +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadOnly, Category = "Command") + FString Key = FString(); + + UPROPERTY(BlueprintReadOnly, Category = "Command") + float Value = 0.f; +}; + +USTRUCT(BlueprintType) +struct FPayload +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadOnly, Category = "Command") + TArray Commands; + + UPROPERTY(BlueprintReadOnly, Category = "Command") + int32 Index = 0; +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCommandReady, const FPayload&, Payload); + +UCLASS() +class LUCKYDATATRANSFER_API ULuckyDataTransferSubsystem : public UWorldSubsystem +{ + GENERATED_BODY() + + +public: + + ULuckyDataTransferSubsystem(); + + virtual void Initialize(FSubsystemCollectionBase& Collection); + + virtual void Deinitialize(); + + TSharedPtr Socket; + + //internal references + + void Internal_OpenWebsocket(const FString& URL, const FString& Protocol); + + FPayload InterpretData(const FString& Message); + + UPROPERTY(BlueprintAssignable) + FCommandReady OnCommandReady; + + + //Callbacks + UFUNCTION() + void Callback_OnConnected(); + + UFUNCTION() + void Callback_OnConnectionError(const FString& Error); + + UFUNCTION() + void Callback_OnMessage(const FString& Message); + + UFUNCTION() + void Internal_OnMessageSent(const FString& Message); + + UFUNCTION() + void Callback_OnConnectionClosed(); + + //Exposed Blueprint Functions + UFUNCTION(BlueprintCallable, Category = "Websocket") + void ConnectToWebsocket(const FString& URL, const FString& Protocol); + + UFUNCTION(BlueprintCallable, Category = "Websocket") + void SendMessage(const FString& Message); + + UFUNCTION() + void CommandReady(const FPayload& Payload); + + //---Observations (Sent to server from Unreal)--------------// + //Image Data declarations + TArray FeatureNames = {}; + FString CameraName = TEXT("front_camera"); + int32 ImageHeight = 480; + int32 ImageWidth = 640; + int32 Fps = 30; + bool bUseVideo = false; + + //Feature Data declarations + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FString ObservationPayloadString = FString(); + + //Internal Functions + bool CreateJsonPayload_Observation(const FObservationPayload& Data); + + //Blueprint Callable Functions + UFUNCTION(BlueprintCallable, Category = "Websocket") + bool MakeObservationPayload(const FObservationPayload& Data); + //---------------------------------------------------------// + +protected: + //LuckyWriteThread* WriteThread = nullptr; +}; diff --git a/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/ObservationData.h b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/ObservationData.h new file mode 100644 index 00000000..736fb2ef --- /dev/null +++ b/Plugins/LuckyDataTransfer/Source/LuckyDataTransfer/Public/ObservationData.h @@ -0,0 +1,80 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "HAL/Runnable.h" +#include "ObservationData.generated.h" + +/** + * + */ +USTRUCT(BlueprintType) +struct FImageShape +{ + GENERATED_USTRUCT_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Observation") + float image_width = 640.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Observation") + float image_height = 480.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Observation") + int32 channel = 3; +}; + +USTRUCT(BlueprintType) +struct FObservationCameraInfo +{ + GENERATED_USTRUCT_BODY() + +public: + UPROPERTY(BlueprintReadWrite, Category = "Observation") + TMap info = { + {"video_fps", "30"}, + {"video_codec", "mp4v"}, + {"video_pix_fmt", "yuv420p"}, + {"video_is_depth_map", "false"}, + {"has_audio", "false"} + }; +}; + +USTRUCT(BlueprintType) +struct FObservationCameraObject +{ + GENERATED_USTRUCT_BODY() + +public: + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FString cameraName = TEXT("front_camera"); + + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FString dtype = TEXT("image"); + + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FImageShape shape = FImageShape(); + + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FString filePath = FString(); +}; + +USTRUCT(BlueprintType) +struct FObservationPayload +{ + GENERATED_USTRUCT_BODY() + +public: + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FString timeStamp = FString(); + + UPROPERTY(BlueprintReadWrite, Category = "Observation") + FString ID = FString(); + + UPROPERTY(BlueprintReadWrite, Category = "Observation") + TMap ObservationState; + + UPROPERTY(BlueprintReadWrite, Category = "Observation") + TArray ObservationCameras; +};