Compare commits
12 Commits
da89e35eb2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 892e282d1d | |||
| adc46114d8 | |||
| c88718fe35 | |||
| 8fe2750a54 | |||
| 33d3ea1e37 | |||
| 0bc765cd29 | |||
| dbe904e388 | |||
| 1f35fcbcf5 | |||
| 65c32d9240 | |||
| 609f623737 | |||
| e02ed8538f | |||
| 9b85bfc94a |
@ -393,15 +393,22 @@ void UDTFluxCoreSubsystem::InitParticipantTracking(const int Bib, const int Cont
|
|||||||
// get all splits
|
// get all splits
|
||||||
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
|
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
|
||||||
FDTFluxSplitSensorKey SplitSensorKey;
|
FDTFluxSplitSensorKey SplitSensorKey;
|
||||||
SplitSensorKey.ContestId = ContestId;
|
|
||||||
SplitSensorKey.StageId = StageId;
|
|
||||||
SplitSensorKey.Bib = Bib;
|
|
||||||
for (auto Split : Contest.Splits)
|
for (auto Split : Contest.Splits)
|
||||||
{
|
{
|
||||||
|
SplitSensorKey = FDTFluxSplitSensorKey();
|
||||||
|
SplitSensorKey.ContestId = ContestId;
|
||||||
|
SplitSensorKey.StageId = StageId;
|
||||||
|
SplitSensorKey.Bib = Bib;
|
||||||
SplitSensorKey.SplitId = Split.SplitId;
|
SplitSensorKey.SplitId = Split.SplitId;
|
||||||
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
|
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
|
||||||
{
|
{
|
||||||
SplitSensorInfos.Add(DataStorage->SplitSensorInfoCache[SplitSensorKey]);
|
FDTFluxSplitSensorInfo SplitSensorInfoToAdd = DataStorage->SplitSensorInfoCache[SplitSensorKey];
|
||||||
|
SplitSensorInfos.Add(SplitSensorInfoToAdd);
|
||||||
|
FString DebugString = FString::Printf(TEXT("SplitSensorInfo for Bib %i "), Bib);
|
||||||
|
DebugString += FString::Printf(TEXT("SplitSensorInfo [Rank] %i "), SplitSensorInfoToAdd.Rank);
|
||||||
|
DebugString += FString::Printf(TEXT("SplitSensorInfo [Rank] %s "), *SplitSensorInfoToAdd.Gap);
|
||||||
|
DebugString += FString::Printf(TEXT("SplitSensorInfo [Rank] %s "), *SplitSensorInfoToAdd.Time);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("SplitSensorInfoCache contains SplitSensorInfo for Bib %i\nData : %s"), Bib, *DebugString);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -410,17 +417,11 @@ void UDTFluxCoreSubsystem::InitParticipantTracking(const int Bib, const int Cont
|
|||||||
}
|
}
|
||||||
FDTFluxSplitHistory History;
|
FDTFluxSplitHistory History;
|
||||||
History.SplitSensors = SplitSensorInfos;
|
History.SplitSensors = SplitSensorInfos;
|
||||||
OnParticipantTrackingReady.Broadcast(History);
|
if (GetParticipant(Bib, History.Participant))
|
||||||
|
{
|
||||||
|
OnParticipantTrackingReady.Broadcast(History);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
FDTFluxSplitHistory SplitHistory;
|
|
||||||
if (GetParticipant(Bib, SplitHistory.Participant))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
FString Text = "sqfhds";
|
|
||||||
FName Key = FName(Text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
|
FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
|
||||||
|
|||||||
@ -28,12 +28,14 @@ void UDTFluxPursuitManager::InitPursuit(const TArray<int> InContestIds, const in
|
|||||||
FDTFluxContest Contest;
|
FDTFluxContest Contest;
|
||||||
if (CoreSubsystem->GetContestForId(ContestId, Contest))
|
if (CoreSubsystem->GetContestForId(ContestId, Contest))
|
||||||
{
|
{
|
||||||
BindRankings();
|
// BindRankings();
|
||||||
FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId());
|
FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId());
|
||||||
FDTFluxStageRankings TempStageRankings;
|
FDTFluxStageRankings TempStageRankings;
|
||||||
//Obtenir les ranking Frais.
|
//Obtenir les ranking Frais.
|
||||||
CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, false);
|
CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, true);
|
||||||
PendingStageRanking.Add(StageKey, false);
|
AllRankings.Add(TempStageRankings);
|
||||||
|
LaunchPursuitSequence();
|
||||||
|
// CoreSubsystem->GetStageRankings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,25 +48,6 @@ void UDTFluxPursuitManager::SetPursuitInfoIsMassStart(FDTFluxPursuitGroup& NextF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext)
|
|
||||||
{
|
|
||||||
FString FocusBibs;
|
|
||||||
for (const auto& Pursuit : OutPursuitFocusNext)
|
|
||||||
{
|
|
||||||
FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext)
|
|
||||||
{
|
|
||||||
FString NextBibs;
|
|
||||||
for (int32 i = 0; i < OutPursuitNext.Num(); i++)
|
|
||||||
{
|
|
||||||
NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
||||||
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
||||||
@ -73,9 +56,9 @@ void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFoc
|
|||||||
FDateTime MetricsStartFunction = FDateTime::UtcNow();
|
FDateTime MetricsStartFunction = FDateTime::UtcNow();
|
||||||
FDateTime CurrentTime = FDateTime::Now();
|
FDateTime CurrentTime = FDateTime::Now();
|
||||||
|
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ==="));
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ==="));
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num());
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num());
|
||||||
|
|
||||||
// BAd Parameter
|
// BAd Parameter
|
||||||
if (MaxSimultaneousPursuit <= 0)
|
if (MaxSimultaneousPursuit <= 0)
|
||||||
@ -111,19 +94,16 @@ void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFoc
|
|||||||
GroupedPursuit.RemoveAt(i);
|
GroupedPursuit.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OutPursuitFocusNext.Reset();
|
OutPursuitFocusNext.Reset();
|
||||||
OutPursuitNext.Reset();
|
OutPursuitNext.Reset();
|
||||||
|
|
||||||
// === VÉRIFICATION CRITIQUE : S'assurer qu'il reste des groupes après suppression ===
|
|
||||||
if (GroupedPursuit.IsEmpty())
|
if (GroupedPursuit.IsEmpty())
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("All groups were expired and removed - no groups available"));
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("All groups were expired and removed - no groups available"));
|
||||||
OutPursuitFocusNext.Reset();
|
OutPursuitFocusNext.Reset();
|
||||||
OutPursuitNext.Reset();
|
OutPursuitNext.Reset();
|
||||||
BIsFocusTruncate = false;
|
BIsFocusTruncate = false;
|
||||||
bIsSequenceDone = true; // Marquer la séquence comme terminée
|
bIsSequenceDone = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +160,6 @@ void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFoc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === LOGS DE RÉSUMÉ ===
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ==="));
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ==="));
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus: %d participants"), OutPursuitFocusNext.Num());
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus: %d participants"), OutPursuitFocusNext.Num());
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next: %d participants"), OutPursuitNext.Num());
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next: %d participants"), OutPursuitNext.Num());
|
||||||
@ -322,7 +301,6 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
{
|
{
|
||||||
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
||||||
{
|
{
|
||||||
// récuperation de la ref de la valeur actuel de la fréquence dans la TMap Freq
|
|
||||||
int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0);
|
int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0);
|
||||||
CurrentFreq = Pair.Value.PursuitGroup.Num();
|
CurrentFreq = Pair.Value.PursuitGroup.Num();
|
||||||
if (CurrentFreq > MaxFrequency)
|
if (CurrentFreq > MaxFrequency)
|
||||||
@ -348,3 +326,25 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
OnPursuitSequenceReady.Broadcast(PursuitData);
|
OnPursuitSequenceReady.Broadcast(PursuitData);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext)
|
||||||
|
{
|
||||||
|
FString FocusBibs;
|
||||||
|
for (const auto& Pursuit : OutPursuitFocusNext)
|
||||||
|
{
|
||||||
|
FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib);
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext)
|
||||||
|
{
|
||||||
|
FString NextBibs;
|
||||||
|
for (int32 i = 0; i < OutPursuitNext.Num(); i++)
|
||||||
|
{
|
||||||
|
NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib);
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs);
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ public class DTFluxProjectSettings : ModuleRules
|
|||||||
"DeveloperSettings",
|
"DeveloperSettings",
|
||||||
"DTFluxCore",
|
"DTFluxCore",
|
||||||
"Settings",
|
"Settings",
|
||||||
"DeveloperSettings"
|
"DeveloperSettings","AvalancheMedia"
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "DTFluxGeneralSettings.h"
|
#include "DTFluxGeneralSettings.h"
|
||||||
|
#include "Assets/DTFluxModelAsset.h"
|
||||||
|
|
||||||
#include "DTFluxProjectSettingsModule.h"
|
#include "DTFluxProjectSettingsModule.h"
|
||||||
|
|
||||||
@ -14,3 +15,17 @@ UDTFluxGeneralSettings::UDTFluxGeneralSettings()
|
|||||||
UE_LOG(logDTFluxProjectSettings, Log, TEXT("Category Name -> %s"), *GetCategoryName().ToString());
|
UE_LOG(logDTFluxProjectSettings, Log, TEXT("Category Name -> %s"), *GetCategoryName().ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxGeneralSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||||
|
{
|
||||||
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||||
|
if (PropertyChangedEvent.Property &&
|
||||||
|
PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UDTFluxGeneralSettings, RemoteTargetRundown))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("RemoteTargetRundown property changed to: %s"),
|
||||||
|
RemoteTargetRundown.IsNull() ? TEXT("None") : *RemoteTargetRundown.ToString());
|
||||||
|
|
||||||
|
OnRemoteRundownChanged.Broadcast(RemoteTargetRundown);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -3,10 +3,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Assets/DTFluxModelAsset.h"
|
|
||||||
#include "Engine/DeveloperSettings.h"
|
#include "Engine/DeveloperSettings.h"
|
||||||
#include "DTFluxGeneralSettings.generated.h"
|
#include "DTFluxGeneralSettings.generated.h"
|
||||||
|
|
||||||
|
class UAvaRundown;
|
||||||
|
class UDTFluxModelAsset;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -20,5 +21,14 @@ public:
|
|||||||
UDTFluxGeneralSettings();
|
UDTFluxGeneralSettings();
|
||||||
UPROPERTY(Category="General", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Datastorage File")
|
UPROPERTY(Category="General", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Datastorage File")
|
||||||
TSoftObjectPtr<UDTFluxModelAsset> ModelAsset;
|
TSoftObjectPtr<UDTFluxModelAsset> ModelAsset;
|
||||||
|
UPROPERTY(Category="General|Remote HTTP", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Rundown Remote Target")
|
||||||
|
TSoftObjectPtr<UAvaRundown> RemoteTargetRundown;
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||||
|
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRemoteRundownChanged, const TSoftObjectPtr<UAvaRundown>& );
|
||||||
|
FOnRemoteRundownChanged OnRemoteRundownChanged;
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,6 +23,8 @@ public class DTFluxRemote : ModuleRules
|
|||||||
"HttpServer",
|
"HttpServer",
|
||||||
"JsonUtilities",
|
"JsonUtilities",
|
||||||
"Json",
|
"Json",
|
||||||
|
"DTFluxProjectSettings",
|
||||||
|
"AvalancheMedia"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
|
|
||||||
#include "DTFluxRemoteSubsystem.h"
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
#include "DTFluxRemoteSubsystem.h"
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
|
||||||
|
#include "DTFluxGeneralSettings.h"
|
||||||
#include "DTFluxRemoteModule.h"
|
#include "DTFluxRemoteModule.h"
|
||||||
#include "DTFluxRemoteModule.h"
|
#include "DTFluxRemoteModule.h"
|
||||||
#include "HttpServerModule.h"
|
#include "HttpServerModule.h"
|
||||||
#include "IHttpRouter.h"
|
#include "IHttpRouter.h"
|
||||||
|
#include "Rundown/AvaRundown.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "JsonObjectConverter.h"
|
||||||
#include "Engine/Engine.h"
|
#include "Engine/Engine.h"
|
||||||
#include "Misc/DateTime.h"
|
#include "Misc/DateTime.h"
|
||||||
|
|
||||||
@ -18,6 +22,16 @@ void UDTFluxRemoteSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
|
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux API Subsystem Initialized"));
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux API Subsystem Initialized"));
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
// S'abonner aux changements de settings
|
||||||
|
if (UDTFluxGeneralSettings* Settings = GetMutableDefault<UDTFluxGeneralSettings>())
|
||||||
|
{
|
||||||
|
SettingsRundownChangedHandle = Settings->OnRemoteRundownChanged.AddUObject(
|
||||||
|
this, &UDTFluxRemoteSubsystem::OnSettingsRundownChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
LoadRundownFromSettings();
|
||||||
// Auto-start server (optionnel)
|
// Auto-start server (optionnel)
|
||||||
StartHTTPServer(63350);
|
StartHTTPServer(63350);
|
||||||
}
|
}
|
||||||
@ -25,7 +39,22 @@ void UDTFluxRemoteSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
void UDTFluxRemoteSubsystem::Deinitialize()
|
void UDTFluxRemoteSubsystem::Deinitialize()
|
||||||
{
|
{
|
||||||
StopHTTPServer();
|
StopHTTPServer();
|
||||||
|
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
// Se désabonner du delegate
|
||||||
|
if (UDTFluxGeneralSettings* Settings = GetMutableDefault<UDTFluxGeneralSettings>())
|
||||||
|
{
|
||||||
|
if (SettingsRundownChangedHandle.IsValid())
|
||||||
|
{
|
||||||
|
Settings->OnRemoteRundownChanged.Remove(SettingsRundownChangedHandle);
|
||||||
|
SettingsRundownChangedHandle.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Décharger proprement le rundown
|
||||||
|
UnloadCurrentRundown();
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux API Subsystem Deinitialized"));
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux API Subsystem Deinitialized"));
|
||||||
|
|
||||||
Super::Deinitialize();
|
Super::Deinitialize();
|
||||||
@ -64,9 +93,9 @@ bool UDTFluxRemoteSubsystem::StartHTTPServer(int32 Port)
|
|||||||
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux HTTP API Server started on port %d"), ServerPort);
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux HTTP API Server started on port %d"), ServerPort);
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("Base URL: http://localhost:%d/dtflux/api/v1"), ServerPort);
|
UE_LOG(logDTFluxRemote, Log, TEXT("Base URL: http://localhost:%d/dtflux/api/v1"), ServerPort);
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("Available routes:"));
|
UE_LOG(logDTFluxRemote, Log, TEXT("Available routes:"));
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT(" POST /dtflux/api/v1/title"));
|
UE_LOG(logDTFluxRemote, Log, TEXT(" PUT /dtflux/api/v1/title"));
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT(" POST /dtflux/api/v1/title-bib"));
|
UE_LOG(logDTFluxRemote, Log, TEXT(" PUT /dtflux/api/v1/title-bib"));
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT(" POST /dtflux/api/v1/commands"));
|
UE_LOG(logDTFluxRemote, Log, TEXT(" PUT /dtflux/api/v1/commands"));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -101,6 +130,16 @@ bool UDTFluxRemoteSubsystem::IsHTTPServerRunning() const
|
|||||||
return bServerRunning;
|
return bServerRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::ResetPendingTitleData()
|
||||||
|
{
|
||||||
|
bHasPendingTitleRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::ResetPendingBibData()
|
||||||
|
{
|
||||||
|
bHasPendingTitleBibRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
void UDTFluxRemoteSubsystem::SetupRoutes()
|
void UDTFluxRemoteSubsystem::SetupRoutes()
|
||||||
{
|
{
|
||||||
if (!HttpRouter.IsValid())
|
if (!HttpRouter.IsValid())
|
||||||
@ -111,21 +150,21 @@ void UDTFluxRemoteSubsystem::SetupRoutes()
|
|||||||
// Route: POST /dtflux/api/v1/title
|
// Route: POST /dtflux/api/v1/title
|
||||||
TitleRouteHandle = HttpRouter->BindRoute(
|
TitleRouteHandle = HttpRouter->BindRoute(
|
||||||
FHttpPath(TEXT("/dtflux/api/v1/title")),
|
FHttpPath(TEXT("/dtflux/api/v1/title")),
|
||||||
EHttpServerRequestVerbs::VERB_GET,
|
EHttpServerRequestVerbs::VERB_PUT,
|
||||||
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleTitleRequest)
|
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleTitleRequest)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Route: POST /dtflux/api/v1/title-bib
|
// Route: POST /dtflux/api/v1/title-bib
|
||||||
TitleBibRouteHandle = HttpRouter->BindRoute(
|
TitleBibRouteHandle = HttpRouter->BindRoute(
|
||||||
FHttpPath(TEXT("/dtflux/api/v1/title-bib")),
|
FHttpPath(TEXT("/dtflux/api/v1/title-bib")),
|
||||||
EHttpServerRequestVerbs::VERB_GET,
|
EHttpServerRequestVerbs::VERB_PUT,
|
||||||
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleTitleBibRequest)
|
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleTitleBibRequest)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Route: POST /dtflux/api/v1/commands
|
// Route: POST /dtflux/api/v1/commands
|
||||||
CommandsRouteHandle = HttpRouter->BindRoute(
|
CommandsRouteHandle = HttpRouter->BindRoute(
|
||||||
FHttpPath(TEXT("/dtflux/api/v1/commands")),
|
FHttpPath(TEXT("/dtflux/api/v1/commands")),
|
||||||
EHttpServerRequestVerbs::VERB_GET,
|
EHttpServerRequestVerbs::VERB_PUT,
|
||||||
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleCommandsRequest)
|
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleCommandsRequest)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -156,9 +195,19 @@ bool UDTFluxRemoteSubsystem::HandleTitleRequest(const FHttpServerRequest& Reques
|
|||||||
AsyncTask(ENamedThreads::GameThread, [this, TitleData]()
|
AsyncTask(ENamedThreads::GameThread, [this, TitleData]()
|
||||||
{
|
{
|
||||||
OnTitleReceived.Broadcast(TitleData);
|
OnTitleReceived.Broadcast(TitleData);
|
||||||
|
if (RemotedRundown && RemotedRundown->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
PendingTitleData = TitleData;
|
||||||
|
bHasPendingTitleRequest = true;
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Playing page %i"), TitleData.RundownPageId);
|
||||||
|
RemotedRundown->PlayPage(TitleData.RundownPageId, EAvaRundownPagePlayType::PlayFromStart);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("No rundown loaded"));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send success response
|
|
||||||
OnComplete(FHttpServerResponse::Create(CreateSuccessResponse(TEXT("Title data received")), TEXT("application/json")));
|
OnComplete(FHttpServerResponse::Create(CreateSuccessResponse(TEXT("Title data received")), TEXT("application/json")));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -211,9 +260,18 @@ bool UDTFluxRemoteSubsystem::HandleCommandsRequest(const FHttpServerRequest& Req
|
|||||||
AsyncTask(ENamedThreads::GameThread, [this, CommandData]()
|
AsyncTask(ENamedThreads::GameThread, [this, CommandData]()
|
||||||
{
|
{
|
||||||
OnCommandReceived.Broadcast(CommandData);
|
OnCommandReceived.Broadcast(CommandData);
|
||||||
|
if (RemotedRundown && RemotedRundown->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
RemotedRundown->StopPage(CommandData.RundownPageId, EAvaRundownPageStopOptions::None, false);
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Stoping page %i"), CommandData.RundownPageId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("No rundown loaded"));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
OnComplete(FHttpServerResponse::Create(CreateSuccessResponse(TEXT("Command data received")), TEXT("application/json")));
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("OK")), TEXT("application/json")));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +279,17 @@ TSharedPtr<FJsonObject> UDTFluxRemoteSubsystem::ParseJsonFromRequest(const FHttp
|
|||||||
{
|
{
|
||||||
// Get request body
|
// Get request body
|
||||||
TArray<uint8> Body = Request.Body;
|
TArray<uint8> Body = Request.Body;
|
||||||
FString JsonString = FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(Body.GetData())));
|
FString JsonString;
|
||||||
|
if (Body.Num() > 0)
|
||||||
|
{
|
||||||
|
// Ajouter un null terminator si nécessaire
|
||||||
|
if (Body.Last() != 0)
|
||||||
|
{
|
||||||
|
Body.Add(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonString = FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(Body.GetData())));
|
||||||
|
}
|
||||||
|
|
||||||
UE_LOG(logDTFluxRemote, Verbose, TEXT("Received JSON: %s"), *JsonString);
|
UE_LOG(logDTFluxRemote, Verbose, TEXT("Received JSON: %s"), *JsonString);
|
||||||
|
|
||||||
@ -271,51 +339,153 @@ bool UDTFluxRemoteSubsystem::ParseTitleData(const TSharedPtr<FJsonObject>& JsonO
|
|||||||
{
|
{
|
||||||
if (!JsonObject.IsValid())
|
if (!JsonObject.IsValid())
|
||||||
{
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Invalid JSON object for %s"), TEXT("FDTFluxRemoteTitleData"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse title fields
|
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRemoteTitleData>(JsonObject.ToSharedRef(), &OutData))
|
||||||
JsonObject->TryGetStringField(TEXT("LastName"), OutData.LastName);
|
{
|
||||||
JsonObject->TryGetStringField(TEXT("FirsName"), OutData.FirstName);
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully parsed %s"), TEXT("FDTFluxRemoteTitleData"));
|
||||||
JsonObject->TryGetStringField(TEXT("Function1"), OutData.Function1);
|
return true;
|
||||||
JsonObject->TryGetStringField(TEXT("Function2"), OutData.Function2);
|
}
|
||||||
|
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("Parsed Title Data - LastName: %s, FirstName: %s"), *OutData.LastName, *OutData.FirstName);
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to convert JSON to %s struct"),TEXT("FDTFluxRemoteTitleData"));
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxRemoteSubsystem::ParseTitleBibData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteBibData& OutData)
|
bool UDTFluxRemoteSubsystem::ParseTitleBibData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteBibData& OutData)
|
||||||
{
|
{
|
||||||
if (!JsonObject.IsValid())
|
if (!JsonObject.IsValid())
|
||||||
{
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Invalid JSON object for %s"), TEXT("FDTFluxRemoteBibData"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject->TryGetNumberField(TEXT("Bib"), OutData.Bib);
|
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRemoteBibData>(JsonObject.ToSharedRef(), &OutData))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully parsed %s"), TEXT("FDTFluxRemoteBibData"));
|
||||||
|
return true;
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("Parsed Title-Bib Data - Bib: %i"), OutData.Bib);
|
}
|
||||||
|
|
||||||
return true;
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to convert JSON to %s struct"),TEXT("FDTFluxRemoteBibData"));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxRemoteSubsystem::ParseCommandData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteCommandData& OutData)
|
bool UDTFluxRemoteSubsystem::ParseCommandData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteCommandData& OutData)
|
||||||
{
|
{
|
||||||
if (!JsonObject.IsValid())
|
if (!JsonObject.IsValid())
|
||||||
{
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Invalid JSON object for %s"), TEXT("FDTFluxRemoteCommandData"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject->TryGetNumberField(TEXT("type"), OutData.Type);
|
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRemoteCommandData>(JsonObject.ToSharedRef(), &OutData))
|
||||||
JsonObject->TryGetStringField(TEXT("Data"), OutData.Data);
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully parsed %s"), TEXT("FDTFluxRemoteCommandData"));
|
||||||
UE_LOG(logDTFluxRemote, Log, TEXT("Parsed Command Data - Command Type: %i, Data: %s"),
|
return true;
|
||||||
OutData.Type, *OutData.Data);
|
}
|
||||||
|
|
||||||
return true;
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to convert JSON to %s struct"),TEXT("FDTFluxRemoteCommandData"));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::UnloadCurrentRundown()
|
||||||
|
{
|
||||||
|
if (RemotedRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Unloading current rundown"));
|
||||||
|
// Ici vous pouvez ajouter une logique de nettoyage si nécessaire
|
||||||
|
// Par exemple : RemotedRundown->StopAllPages();
|
||||||
|
RemotedRundown = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::LoadRundownFromSettings()
|
||||||
|
{
|
||||||
|
const UDTFluxGeneralSettings* Settings = GetDefault<UDTFluxGeneralSettings>();
|
||||||
|
if (!Settings)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("Cannot access DTFlux settings"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSoftObjectPtr<UAvaRundown> RundownAsset = Settings->RemoteTargetRundown;
|
||||||
|
|
||||||
|
if (RundownAsset.IsNull())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("No rundown specified in settings"));
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemotedRundown && RemotedRundown == RundownAsset.LoadSynchronous())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Rundown already loaded: %s"), *RundownAsset.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Décharger l'ancien rundown d'abord
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
RundownAsset = RundownAsset.LoadSynchronous();
|
||||||
|
// Charger le nouveau rundown
|
||||||
|
if ( RundownAsset.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully loaded rundown from settings: %s"), *RundownAsset.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to load rundown from settings: %s"), *RundownAsset.ToString());
|
||||||
|
}
|
||||||
|
LoadRundown(RundownAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::LoadRundown(const TSoftObjectPtr<UAvaRundown>& RundownAsset)
|
||||||
|
{
|
||||||
|
if (RundownAsset.IsNull())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("Cannot load rundown: asset is null"));
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Charger le rundown de manière synchrone
|
||||||
|
UAvaRundown* LoadedRundown = RundownAsset.LoadSynchronous();
|
||||||
|
|
||||||
|
// Vérifier si le rundown est déjà chargé
|
||||||
|
if (RemotedRundown && RemotedRundown == LoadedRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Rundown already loaded: %s"), *RundownAsset.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Décharger l'ancien rundown d'abord
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
|
||||||
|
// Assigner le nouveau rundown
|
||||||
|
RemotedRundown = LoadedRundown;
|
||||||
|
|
||||||
|
// Vérifier que le chargement a réussi
|
||||||
|
if (RemotedRundown && RemotedRundown->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
RemotedRundown->InitializePlaybackContext();
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully loaded rundown: %s"), *RundownAsset.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to load rundown: %s"), *RundownAsset.ToString());
|
||||||
|
RemotedRundown = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
void UDTFluxRemoteSubsystem::OnSettingsRundownChanged(const TSoftObjectPtr<UAvaRundown>& NewRundown)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Manual processing functions for testing
|
// Manual processing functions for testing
|
||||||
bool UDTFluxRemoteSubsystem::ProcessTitleData(const FString& JsonString)
|
bool UDTFluxRemoteSubsystem::ProcessTitleData(const FString& JsonString)
|
||||||
{
|
{
|
||||||
@ -373,4 +543,7 @@ bool UDTFluxRemoteSubsystem::ProcessCommandData(const FString& JsonString)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
220
Source/DTFluxRemote/Private/DTFluxRemotedLevelController.cpp
Normal file
220
Source/DTFluxRemote/Private/DTFluxRemotedLevelController.cpp
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
// DTFluxRemoteActor.cpp
|
||||||
|
#include "DTFluxRemotedLevelController.h"
|
||||||
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
|
||||||
|
ADTFluxRemotedLevelController::ADTFluxRemotedLevelController()
|
||||||
|
{
|
||||||
|
PrimaryActorTick.bCanEverTick = false;
|
||||||
|
RemoteSubsystem = nullptr;
|
||||||
|
bEventsBound = false;
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Constructor called"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::PostInitializeComponents()
|
||||||
|
{
|
||||||
|
Super::PostInitializeComponents();
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: PostInitializeComponents called"));
|
||||||
|
|
||||||
|
// Essayer de bind dès que possible
|
||||||
|
InitializeSubsystemBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: BeginPlay called"));
|
||||||
|
|
||||||
|
// S'assurer que le binding est fait (au cas où PostInitializeComponents aurait échoué)
|
||||||
|
if (!bEventsBound)
|
||||||
|
{
|
||||||
|
InitializeSubsystemBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::InitializeSubsystemBinding()
|
||||||
|
{
|
||||||
|
// Éviter le double binding
|
||||||
|
if (bEventsBound)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Events already bound, skipping"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer le subsystem
|
||||||
|
if (UWorld* World = GetWorld())
|
||||||
|
{
|
||||||
|
RemoteSubsystem = GEngine->GetEngineSubsystem<UDTFluxRemoteSubsystem>();
|
||||||
|
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
// Bind les events du subsystem
|
||||||
|
RemoteSubsystem->OnTitleReceived.AddDynamic(
|
||||||
|
this, &ADTFluxRemotedLevelController::OnTitleDataReceived
|
||||||
|
);
|
||||||
|
RemoteSubsystem->OnTitleBibReceived.AddDynamic(
|
||||||
|
this, &ADTFluxRemotedLevelController::OnTitleBibDataReceived
|
||||||
|
);
|
||||||
|
|
||||||
|
RemoteSubsystem->OnCommandReceived.AddDynamic(
|
||||||
|
this, &ADTFluxRemotedLevelController::OnCommandDataReceived
|
||||||
|
);
|
||||||
|
|
||||||
|
bEventsBound = true;
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Successfully bound to subsystem events"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: DTFluxRemoteSubsystem not available yet"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: World not available yet"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::EnsureSubsystemBinding()
|
||||||
|
{
|
||||||
|
if (!bEventsBound)
|
||||||
|
{
|
||||||
|
InitializeSubsystemBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
|
{
|
||||||
|
// Unbind les events pour éviter les fuites mémoire
|
||||||
|
if (RemoteSubsystem && bEventsBound)
|
||||||
|
{
|
||||||
|
if (TitleReceivedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RemoteSubsystem->OnTitleReceived.RemoveDynamic(this, &ADTFluxRemotedLevelController::OnTitleDataReceived);
|
||||||
|
TitleReceivedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TitleBibReceivedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RemoteSubsystem->OnTitleBibReceived.RemoveDynamic(this, &ADTFluxRemotedLevelController::OnTitleBibDataReceived);
|
||||||
|
TitleBibReceivedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CommandReceivedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RemoteSubsystem->OnCommandReceived.RemoveDynamic(this, &ADTFluxRemotedLevelController::OnCommandDataReceived);
|
||||||
|
CommandReceivedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bEventsBound = false;
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Unbound from subsystem events"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::EndPlay(EndPlayReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::OnTitleDataReceived(const FDTFluxRemoteTitleData& TitleData)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Received Title Data - %s %s (RundownPageId: %d)"),
|
||||||
|
*TitleData.FirstName, *TitleData.LastName, TitleData.RundownPageId);
|
||||||
|
|
||||||
|
// Broadcast l'event Blueprint
|
||||||
|
OnTitleReceived.Broadcast(TitleData);
|
||||||
|
|
||||||
|
// Appeler l'event Blueprint implémentable
|
||||||
|
BP_OnTitleDataReceived(TitleData);
|
||||||
|
|
||||||
|
// Appeler la fonction virtuelle C++ (peut être overridée dans les classes dérivées)
|
||||||
|
HandleTitleData(TitleData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::OnTitleBibDataReceived(const FDTFluxRemoteBibData& BibData)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Received Title Bib Data - Bib: %d"), BibData.Bib);
|
||||||
|
|
||||||
|
// Broadcast l'event Blueprint
|
||||||
|
OnTitleBibReceived.Broadcast(BibData);
|
||||||
|
|
||||||
|
// Appeler l'event Blueprint implémentable
|
||||||
|
BP_OnTitleBibDataReceived(BibData);
|
||||||
|
|
||||||
|
// Appeler la fonction virtuelle C++ (peut être overridée dans les classes dérivées)
|
||||||
|
HandleTitleBibData(BibData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::OnCommandDataReceived(const FDTFluxRemoteCommandData& CommandData)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Received Command Data - Type: %s, : RundownPageId %i"),
|
||||||
|
*CommandData.Type, CommandData.RundownPageId);
|
||||||
|
|
||||||
|
// Broadcast l'event Blueprint
|
||||||
|
OnCommandReceived.Broadcast(CommandData);
|
||||||
|
|
||||||
|
// Appeler l'event Blueprint implémentable
|
||||||
|
BP_OnCommandDataReceived(CommandData);
|
||||||
|
|
||||||
|
// Appeler la fonction virtuelle C++ (peut être overridée dans les classes dérivées)
|
||||||
|
HandleCommandData(CommandData);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ADTFluxRemotedLevelController::IsSubsystemAvailable() const
|
||||||
|
{
|
||||||
|
return RemoteSubsystem && RemoteSubsystem->IsValidLowLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ADTFluxRemotedLevelController::IsHTTPServerRunning() const
|
||||||
|
{
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
return RemoteSubsystem->IsHTTPServerRunning();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::StartHTTPServer(int32 Port)
|
||||||
|
{
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
RemoteSubsystem->StartHTTPServer(Port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: Cannot start HTTP server - subsystem not available"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::StopHTTPServer()
|
||||||
|
{
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
RemoteSubsystem->StopHTTPServer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: Cannot stop HTTP server - subsystem not available"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implémentations par défaut des fonctions virtuelles C++
|
||||||
|
void ADTFluxRemotedLevelController::HandleTitleData_Implementation(const FDTFluxRemoteTitleData& TitleData)
|
||||||
|
{
|
||||||
|
// Implémentation par défaut - peut être overridée dans les classes dérivées
|
||||||
|
UE_LOG(LogTemp, Verbose, TEXT("DTFluxRemoteActor: Handling Title Data (default implementation)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::HandleTitleBibData_Implementation(const FDTFluxRemoteBibData& BibData)
|
||||||
|
{
|
||||||
|
// Implémentation par défaut - peut être overridée dans les classes dérivées
|
||||||
|
UE_LOG(LogTemp, Verbose, TEXT("DTFluxRemoteActor: Handling Title Bib Data (default implementation)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::HandleCommandData_Implementation(const FDTFluxRemoteCommandData& CommandData)
|
||||||
|
{
|
||||||
|
// Implémentation par défaut - peut être overridée dans les classes dérivées
|
||||||
|
UE_LOG(LogTemp, Verbose, TEXT("DTFluxRemoteActor: Handling Command Data (default implementation)"));
|
||||||
|
}
|
||||||
@ -14,6 +14,8 @@ public:
|
|||||||
|
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
FDateTime UpdateAt = FDateTime::Now();
|
FDateTime UpdateAt = FDateTime::Now();
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
int RundownPageId = -1;
|
||||||
FDTFluxRemoteBasicData() = default;
|
FDTFluxRemoteBasicData() = default;
|
||||||
FDTFluxRemoteBasicData(const FDateTime& InUpdateAt): UpdateAt(InUpdateAt){};
|
FDTFluxRemoteBasicData(const FDateTime& InUpdateAt): UpdateAt(InUpdateAt){};
|
||||||
};
|
};
|
||||||
@ -38,6 +40,7 @@ public:
|
|||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
FString Function2 = "";
|
FString Function2 = "";
|
||||||
|
|
||||||
|
|
||||||
FDTFluxRemoteTitleData() = default;
|
FDTFluxRemoteTitleData() = default;
|
||||||
FDTFluxRemoteTitleData(const FString InFirstName, const FString InLastName, const FString InFunction1, const FString InFunction2):
|
FDTFluxRemoteTitleData(const FString InFirstName, const FString InLastName, const FString InFunction1, const FString InFunction2):
|
||||||
FirstName(InFirstName), LastName(InLastName), Function1(InFunction1), Function2(InFunction2){};
|
FirstName(InFirstName), LastName(InLastName), Function1(InFunction1), Function2(InFunction2){};
|
||||||
@ -64,12 +67,10 @@ public:
|
|||||||
FDTFluxRemoteCommandData() = default;
|
FDTFluxRemoteCommandData() = default;
|
||||||
|
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
int Type = -1;
|
FString Type = "Stop";
|
||||||
|
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
|
||||||
FString Data = "";
|
|
||||||
|
|
||||||
FDTFluxRemoteCommandData(int InType, FString InData):
|
FDTFluxRemoteCommandData(FString InType):
|
||||||
Type(InType), Data(InData){};
|
Type(InType){};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "IHttpRouter.h"
|
#include "IHttpRouter.h"
|
||||||
#include "DTFluxRemoteSubsystem.generated.h"
|
#include "DTFluxRemoteSubsystem.generated.h"
|
||||||
|
|
||||||
|
class UAvaRundown;
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTitleReceived, const FDTFluxRemoteTitleData&, TitleData);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTitleReceived, const FDTFluxRemoteTitleData&, TitleData);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTitleBibReceived, const FDTFluxRemoteBibData&, TitleBibData);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTitleBibReceived, const FDTFluxRemoteBibData&, TitleBibData);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCommandReceived, const FDTFluxRemoteCommandData&, CommandData);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCommandReceived, const FDTFluxRemoteCommandData&, CommandData);
|
||||||
@ -21,7 +22,6 @@ class DTFLUXREMOTE_API UDTFluxRemoteSubsystem : public UEngineSubsystem
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
public:
|
public:
|
||||||
public:
|
|
||||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
virtual void Deinitialize() override;
|
virtual void Deinitialize() override;
|
||||||
|
|
||||||
@ -56,6 +56,25 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
bool ProcessCommandData(const FString& JsonString);
|
bool ProcessCommandData(const FString& JsonString);
|
||||||
|
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
bool bHasPendingTitleRequest = false;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
bool bHasPendingTitleBibRequest = false;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
FDTFluxRemoteTitleData PendingTitleData;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
FDTFluxRemoteBibData PendingTitleBibData;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
void ResetPendingTitleData();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
void ResetPendingBibData();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupRoutes();
|
void SetupRoutes();
|
||||||
|
|
||||||
@ -73,10 +92,26 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
TSharedPtr<IHttpRouter> HttpRouter;
|
TSharedPtr<IHttpRouter> HttpRouter;
|
||||||
|
TSoftObjectPtr<UAvaRundown> RemotedRundown;
|
||||||
int32 ServerPort = 63350;
|
int32 ServerPort = 63350;
|
||||||
bool bServerRunning = false;
|
bool bServerRunning = false;
|
||||||
|
|
||||||
FHttpRouteHandle TitleRouteHandle;
|
FHttpRouteHandle TitleRouteHandle;
|
||||||
FHttpRouteHandle TitleBibRouteHandle;
|
FHttpRouteHandle TitleBibRouteHandle;
|
||||||
FHttpRouteHandle CommandsRouteHandle;
|
FHttpRouteHandle CommandsRouteHandle;
|
||||||
|
|
||||||
|
void UnloadCurrentRundown();
|
||||||
|
void LoadRundownFromSettings();
|
||||||
|
bool LoadRundown(const TSoftObjectPtr<UAvaRundown>& RundownAsset);
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
FDelegateHandle SettingsRundownChangedHandle;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
// Callback pour les changements de settings
|
||||||
|
UFUNCTION()
|
||||||
|
void OnSettingsRundownChanged(const TSoftObjectPtr<UAvaRundown>& NewRundown);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
94
Source/DTFluxRemote/Public/DTFluxRemotedLevelController.h
Normal file
94
Source/DTFluxRemote/Public/DTFluxRemotedLevelController.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// DTFluxRemotedLevelController.h
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/Actor.h"
|
||||||
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
#include "DTFluxRemotedLevelController.generated.h"
|
||||||
|
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class DTFLUXREMOTE_API ADTFluxRemotedLevelController : public AActor
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
ADTFluxRemotedLevelController();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void PostInitializeComponents() override;
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||||
|
|
||||||
|
// Subsystem et binding
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux")
|
||||||
|
UDTFluxRemoteSubsystem* RemoteSubsystem;
|
||||||
|
|
||||||
|
FDelegateHandle TitleReceivedHandle;
|
||||||
|
FDelegateHandle TitleBibReceivedHandle;
|
||||||
|
FDelegateHandle CommandReceivedHandle;
|
||||||
|
bool bEventsBound;
|
||||||
|
|
||||||
|
// Fonctions de binding
|
||||||
|
void InitializeSubsystemBinding();
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Callbacks avec UFUNCTION()
|
||||||
|
UFUNCTION()
|
||||||
|
void OnTitleDataReceived(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnTitleBibDataReceived(const FDTFluxRemoteBibData& BibData);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnCommandDataReceived(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Events Blueprint-friendly
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux Events")
|
||||||
|
FOnTitleReceived OnTitleReceived;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux Events")
|
||||||
|
FOnTitleBibReceived OnTitleBibReceived;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux Events")
|
||||||
|
FOnCommandReceived OnCommandReceived;
|
||||||
|
|
||||||
|
// Fonctions utilitaires
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool IsSubsystemAvailable() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool IsHTTPServerRunning() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
void StartHTTPServer(int32 Port = 63350);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
void StopHTTPServer();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
void EnsureSubsystemBinding();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Events Blueprint implémentables
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "DTFlux Events")
|
||||||
|
void BP_OnTitleDataReceived(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "DTFlux Events")
|
||||||
|
void BP_OnTitleBibDataReceived(const FDTFluxRemoteBibData& BibData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "DTFlux Events")
|
||||||
|
void BP_OnCommandDataReceived(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
|
||||||
|
// Fonctions virtuelles C++
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category = "DTFlux Events")
|
||||||
|
void HandleTitleData(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
virtual void HandleTitleData_Implementation(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category = "DTFlux Events")
|
||||||
|
void HandleTitleBibData(const FDTFluxRemoteBibData& BibData);
|
||||||
|
virtual void HandleTitleBibData_Implementation(const FDTFluxRemoteBibData& BibData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category = "DTFlux Events")
|
||||||
|
void HandleCommandData(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
virtual void HandleCommandData_Implementation(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
};
|
||||||
@ -1,7 +1,7 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
#include "FTDFluxUtils.h"
|
#include "DTFluxUtils.h"
|
||||||
|
|
||||||
#include "DTFluxCoreSubsystem.h"
|
#include "DTFluxCoreSubsystem.h"
|
||||||
#include "DTFluxUtilitiesModule.h"
|
#include "DTFluxUtilitiesModule.h"
|
||||||
@ -55,3 +55,13 @@ void UFTDFluxUtils::GetFullName(const int Bib, FText& OutFullName)
|
|||||||
}
|
}
|
||||||
UE_LOG(logDTFluxUtilities, Error, TEXT("DTFluxCoreSubsystem not available"));
|
UE_LOG(logDTFluxUtilities, Error, TEXT("DTFluxCoreSubsystem not available"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TArray<FDTFluxSplitSensorInfo> UFTDFluxUtils::SortSplitRankingsByRank(const TArray<FDTFluxSplitSensorInfo>& Rankings)
|
||||||
|
{
|
||||||
|
TArray<FDTFluxSplitSensorInfo> CopyRankings = Rankings;
|
||||||
|
CopyRankings.Sort([](const FDTFluxSplitSensorInfo& A, const FDTFluxSplitSensorInfo& B)
|
||||||
|
{
|
||||||
|
return A.Rank < B.Rank;
|
||||||
|
});
|
||||||
|
return CopyRankings;
|
||||||
|
}
|
||||||
@ -5,8 +5,8 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h"
|
#include "DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h"
|
||||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
#include "Types/Struct/DTFluxRankingStructs.h"
|
#include "Types/Struct/DTFluxSplitSensor.h"
|
||||||
#include "FTDFluxUtils.generated.h"
|
#include "DTFluxUtils.generated.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -70,4 +70,8 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils")
|
||||||
static void GetFullName(const int Bib, FText& OutFullName);
|
static void GetFullName(const int Bib, FText& OutFullName);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils")
|
||||||
|
static TArray<FDTFluxSplitSensorInfo> SortSplitRankingsByRank(const TArray<FDTFluxSplitSensorInfo>& Rankings);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user