diff --git a/Source/DTFluxAPI/DTFluxAPI.Build.cs b/Source/DTFluxAPI/DTFluxAPI.Build.cs index 7bbf7f7..3c944de 100644 --- a/Source/DTFluxAPI/DTFluxAPI.Build.cs +++ b/Source/DTFluxAPI/DTFluxAPI.Build.cs @@ -11,7 +11,7 @@ public class DTFluxAPI : ModuleRules PublicDependencyModuleNames.AddRange( new string[] { - "Core", + "Core", "AvalancheText", } ); @@ -20,6 +20,8 @@ public class DTFluxAPI : ModuleRules { "CoreUObject", "Engine", + "EditorSubsystem", + "EditorFramework", "Slate", "SlateCore", "HTTPServer", @@ -31,8 +33,11 @@ public class DTFluxAPI : ModuleRules "DeveloperSettings", "Json", "JsonUtilities", + "SlateCore", + "Text3D", "AvalancheCore", "AvalancheMedia", + "AvalancheText", } ); } diff --git a/Source/DTFluxAPI/Private/DTFluxCountDown/DTFluxCountDownComponent.cpp b/Source/DTFluxAPI/Private/DTFluxCountDown/DTFluxCountDownComponent.cpp new file mode 100644 index 0000000..e91202c --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxCountDown/DTFluxCountDownComponent.cpp @@ -0,0 +1,84 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxCountDown/DTFluxCountDownComponent.h" + +#include "DTFluxAPILog.h" + + +// Sets default values for this component's properties +UDTFluxCountDownComponent::UDTFluxCountDownComponent() +{ + // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features + // off to improve performance if you don't need them. + PrimaryComponentTick.bCanEverTick = true; + + // ... +} + + +// Called when the game starts +void UDTFluxCountDownComponent::BeginPlay() +{ + Super::BeginPlay(); + + + UWorld* World = GetWorld(); + if(World) + { + World->GetTimerManager(). + SetTimer(WaitingTimer, this, + &UDTFluxCountDownComponent::WaitingTimerFn, WaitingRate, true); + } + // ... + +} + + + + +// Called every frame +void UDTFluxCountDownComponent::TickComponent(float DeltaTime, ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction) +{ + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + // ... +} + +void UDTFluxCountDownComponent::SetGoTime(FDateTime NewGoTime) +{ + GoTime = NewGoTime; +} + +void UDTFluxCountDownComponent::SetDuration(int32 NewDuration) +{ + // Need to be protected while counting + Duration = NewDuration; +} + +void UDTFluxCountDownComponent::SetTarget(UAvaText3DComponent* TextComponent) +{ + // Need to be protected while counting + TextRef = TextComponent; + if(IsValid(TextRef)) + { + FText WaitingText = FText::FromString("WAITING !!!"); + + TextRef->SetText(WaitingText); + + UE_LOG(LogDTFluxAPI, Log, TEXT("Setting TextRef to %s"), *TextRef->GetText().ToString()); + } +} + +void UDTFluxCountDownComponent::CountUpTimerFn() +{ + +} + +void UDTFluxCountDownComponent::WaitingTimerFn() +{ + + UE_LOG(LogDTFluxAPI, Log, TEXT("WAITING")); +} + diff --git a/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp b/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp index e864580..6da26ce 100644 --- a/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp +++ b/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp @@ -7,87 +7,6 @@ #include "DTFluxAPILog.h" #include "DTFluxModel/DTFluxModel.h" -bool UDTFluxDataStorage::UpdateDataStorage(const FString JsonPayload) -{ - UE_LOG(LogDTFluxAPI, Log, TEXT("UDPATE DataStorage with data : %s"), *JsonPayload); - // Single json value (any of supported json types e.g. - // object with properties, array, bool) at top level of json - TSharedPtr JsonValue; - - // Create a reader pointer to read the json data - TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonPayload); - if (FJsonSerializer::Deserialize(Reader, JsonValue)) { - // Get the value of the json object by field name - TSharedPtr Json = JsonValue->AsObject(); - FString Type = Json->GetStringField(TEXT("type")); - TArray> Datas = Json->GetArrayField(TEXT("datas")); - // UE_LOG(LogDTFluxAPI, Log, TEXT("DTFlux-Response-Type : %s"), *Type); - if(Type.Contains("race-datas")) - { - UE_LOG(LogDTFluxAPI, Log, TEXT("DTFlux-Response-Type : \"race-datas\"")); - // Contests is empty; - if(Contests.Num() == 0) - { - FDTFluxContest Contest; - for(const auto& Data : Datas) - { - TSharedPtr ContestData = Data->AsObject(); - Contest.Id = ContestData->GetIntegerField(TEXT("id")); - Contest.Name = ContestData->GetStringField(TEXT("name")); - Contest.SetDate(ContestData->GetStringField(TEXT("date"))); - TArray Splits; - TArray> SplitDatas = ContestData->GetArrayField(TEXT("splits")); - for(const auto& SplitData : SplitDatas) - { - FDTFluxSplit Split; - Split.Id = SplitData->AsObject()->GetIntegerField(TEXT("id")); - Split.Name = SplitData->AsObject()->GetStringField(TEXT("name")); - Splits.Add(Split); - } - TArray> StagesData = ContestData->GetArrayField(TEXT("stages")); - Contest.AddStage(StagesData, Splits); - } - } - - } - else if(Type.Contains("team-list")) - { - UE_LOG(LogDTFluxAPI, Log, TEXT("DTFlux-Response-Type : \"team-list\"")); - - } - else if(Type.Contains("contest-ranking")) - { - int ContestId = Json->GetIntegerField(TEXT("contestID")); - UE_LOG(LogDTFluxAPI, Log, TEXT("DTFlux-Response-Type : \"contest-ranking\"")); - - } - else if(Type.Contains("stage-ranking")) - { - int ContestId = Json->GetIntegerField(TEXT("contestID")); - int StageId = Json->GetIntegerField(TEXT("stageID")); - int SplitID = -1; - if(Json->HasField(TEXT("splitID"))) - { - SplitID = Json->GetIntegerField(TEXT("splitID")); - if(SplitID == -1) - { - // we have all splits gaps from the request - } - } - UE_LOG(LogDTFluxAPI, Log, TEXT("DTFlux-Response-Type : \"stage-ranking\"")); - - }else if(Type.Contains("split-sensor")) - { - UE_LOG(LogDTFluxAPI, Log, TEXT("split-sensor received")); - // Request New Ranking - // - } - - } - return true; -} - - TArray UDTFluxDataStorage::GetStages(const int ContestId) { @@ -99,6 +18,126 @@ TArray UDTFluxDataStorage::GetStages(const int ContestId) return Stages; } + + +void UDTFluxDataStorage::UpdateSplitRanking(const FDTFluxStageRankingResponse& SplitRankingResponse) +{ + for(auto& Contest : Contests) + { + if(Contest.Id == SplitRankingResponse.ContestID) + { + for(auto& Stage : Contest.Stages) + { + if(Stage.Id == SplitRankingResponse.StageID) + { + for(auto& Split : Stage.Splits) + { + if(Split.Id == SplitRankingResponse.SplitID) + { + for(auto& SplitRankingItemResp : SplitRankingResponse.Datas) + { + Split.InsertOrReplace(SplitRankingItemResp); + } + } + + } + } + } + } + } +} + +// // Bad implementation +// void UDTFluxDataStorage::UpdateParticipant(const FDTFluxTeamUpdateResponse& TeamUpdateResponse) +// { +// FDTFluxParticipant Participant; +// +// for(auto& TeamUpdateRespItem : TeamUpdateResponse.Datas) +// { +// Participant.Bib = TeamUpdateRespItem.Bib; +// Participant.Category = TeamUpdateRespItem.Category; +// Participant.Club = TeamUpdateRespItem.Club; +// Participant.Elite = TeamUpdateRespItem.Elite; +// Participant.Person1.Gender = TeamUpdateRespItem.Gender; +// Participant.Person1.FirstName = TeamUpdateRespItem.FirstName; +// Participant.Person1.LastName = TeamUpdateRespItem.LastName; +// // TODO ??? +// // Participant.Person2.Gender = TeamListItemResponse.Gender2; +// Participant.Person2.FirstName = TeamUpdateRespItem.FirstName2; +// Participant.Person2.LastName = TeamUpdateRespItem.LastName2; +// Participant.Status = static_cast>(TeamUpdateRespItem.Status); +// bool Update = false; +// for(auto& Contest : Contests) +// { +// int Idx = 0; +// for(auto& OldParticipant : Contest.Participants) +// { +// if(OldParticipant.Bib == Participant.Bib) +// { +// Update = true; +// UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d OLD : %s %s New : %s %s In Contest%02d"), +// Idx, *OldParticipant.Person1.FirstName, *OldParticipant.Person1.LastName, +// *Participant.Person1.FirstName, *Participant.Person1.LastName, Contest.Id); +// break; +// } +// Idx++; +// } +// if(Update) +// { +// if(Contest.Participants.IsValidIndex(Idx)) +// { +// UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, to be removed : %s %s in Contest%02d"), +// Idx, *Contest.Participants[Idx].Person1.FirstName, *Contest.Participants[Idx].Person1.LastName, +// Contest.Id); +// Contest.Participants.RemoveAt(Idx); +// } +// } +// Contest.Participants.Add(Participant); +// +// } +// } +// } + +// TODO NOT IMPLEMENTED +void UDTFluxDataStorage::UpdateParticipantStatus(const FDTFluxStatusUpdateResponse& StatusUpdateResponse) +{ + EDTFluxParticipantStatusType NewStatus = static_cast>(StatusUpdateResponse.Datas.Status); + UE_LOG(LogDTFluxAPI, Log, TEXT("Status to be UPDATED for Bib %d to %s"), StatusUpdateResponse.Datas.Bib, + *UEnum::GetValueAsString(NewStatus)); + bool Found = false; + for(auto& Contest : Contests) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Checking Participant witg Bib %d in Contest %02d"), + StatusUpdateResponse.Datas.Bib, Contest.Id); + for(auto& Participant: Contest.Participants) + { + if(Participant.Bib == StatusUpdateResponse.Datas.Bib) + { + Found = true; + EDTFluxParticipantStatusType OldStatus = Participant.Status; + Participant.Status = static_cast>(StatusUpdateResponse.Datas.Status); + UE_LOG(LogDTFluxAPI, Log, TEXT("Status UPDATED from %s to %s"), + *UEnum::GetValueAsString(OldStatus), *UEnum::GetValueAsString(Participant.Status)); + return; + } + } + } +} + +// TODO NOT IMPLEMENTED +bool UDTFluxDataStorage::IsFinisherSplit(const FDTFluxSplitSensorResponse& SplitSensorResponse) +{ + return true; +} + +// TODO NOT IMPLEMENTED +FDTFluxFinisher UDTFluxDataStorage::GetFinisherStatus(const FDTFluxSplitSensorResponse& SplitSensorResponse) +{ + FDTFluxFinisher Finisher; + Finisher.Participant.Bib = 15222; + return Finisher; +} + bool UDTFluxDataStorage::GetContest(FDTFluxContest& OutContest, const int& ContestId) { // Current contest requested @@ -163,7 +202,6 @@ bool UDTFluxDataStorage::GetStage(FDTFluxStage& CurrentStage, const int& StageId } } } - return false; } @@ -185,9 +223,16 @@ TArray UDTFluxDataStorage::GetParticipants(const int Contest return Participants; } -FDTFluxParticipant UDTFluxDataStorage::GetParticipant(const int ContestID, const int ParticipantBib) +void UDTFluxDataStorage::GetParticipant(const int ContestID, const int ParticipantBib, FDTFluxParticipant& OutParticipant) { - return GetParticipants(ContestID)[ParticipantBib]; + TArray Particpants = GetParticipants(ContestID); + for(auto& Participant : Particpants) + { + if(Participant.Bib == ParticipantBib) + { + OutParticipant = Participant; + } + } } TArray UDTFluxDataStorage::GetStageRanking(const int ContestId, const int StageId) @@ -208,24 +253,28 @@ void UDTFluxDataStorage::AddOrUpdateContest(const FDTFluxContestResponse& Contes { FDTFluxContest Contest; bool NewContest = false; + int ContestIdx = 0; if(!Contests.IsEmpty() ) { for(auto& OldContest: Contests) { + NewContest = true; if(OldContest.Id == ContestResponse.Id) { + // We have the contest that need to be updated Contest = OldContest; NewContest = false; break; - }else - { - NewContest = true; } + // updating ContestIndex + ContestIdx++; } }else { + // this is a new contest because we don't have one NewContest = true; } + // Updating values Contest.Id = ContestResponse.Id; Contest.Name = ContestResponse.Name; TArray Splits; @@ -248,15 +297,22 @@ void UDTFluxDataStorage::AddOrUpdateContest(const FDTFluxContestResponse& Contes } if(NewContest) { + Contest.Dump(); Contests.Add(Contest); + return; } - // UE_LOG(LogDTFluxAPI, Log, TEXT("Contest DUMP %s")); - + Contest.Dump(); + Contests.RemoveAt(ContestIdx); + Contests.Insert(Contest, ContestIdx); + // handle updating contest } void UDTFluxDataStorage::AddOrUpdateParticipant(const FDTFluxTeamListItemResponse& TeamListItemResponse) { - // UE_LOG(LogDTFluxAPI, Log, TEXT("About to process Participant %s BIB : %d"), *TeamListItemResponse.LastName, TeamListItemResponse.Bib); + UE_LOG(LogDTFluxAPI, Log, TEXT("In DataStorage::AddOrUpdateParticipant")); + UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdateOrAdd Participant %d %s %s in Contest%02d "), + TeamListItemResponse.Bib, *TeamListItemResponse.FirstName, *TeamListItemResponse.LastName, + TeamListItemResponse.ContestId); FDTFluxParticipant Participant; Participant.Bib = TeamListItemResponse.Bib; Participant.Category = TeamListItemResponse.Category; @@ -269,20 +325,91 @@ void UDTFluxDataStorage::AddOrUpdateParticipant(const FDTFluxTeamListItemRespons // Participant.Person2.Gender = TeamListItemResponse.Gender2; Participant.Person2.FirstName = TeamListItemResponse.FirstName2; Participant.Person2.LastName = TeamListItemResponse.LastName2; - Participant.Status = TeamListItemResponse.Status; + Participant.Status = static_cast>(TeamListItemResponse.Status); for(auto& Contest: Contests) { if(Contest.Id == TeamListItemResponse.ContestId) { + UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdateOrAdd Participant %d %s %s in Contest%02d "), + Participant.Bib, *Participant.Person1.FirstName, *Participant.Person1.LastName, + Contest.Id); + Contest.AddParticipant(Participant); return; } - } - // UE_LOG(LogDTFluxAPI, Error, TEXT(" Participant %s with BIB : %d Has no valid Contest associated. Got %d. This participant wil not be registered."), - // *Participant.Person1.LastName, Participant.Bib, TeamListItemResponse.ContestId); + UE_LOG(LogDTFluxAPI, Log, TEXT("Contest%02d has now %04d Participants"), Contest.Id, + Contest.Participants.Num()); + } } +void UDTFluxDataStorage::UpdateContestRanking(const FDTFluxContestRankingResponse& InContestRanking) +{ + + for(auto& Contest : Contests) + { + if(Contest.Id == InContestRanking.ContestID) + { + // Clean ContestRanking + Contest.ContestRanking.Empty(); + for(const auto& TeamContestRankingResponse : InContestRanking.Datas) + { + FDTFluxContestRanking NewRankingEl; + NewRankingEl.Bib = TeamContestRankingResponse.Bib; + NewRankingEl.Rank = TeamContestRankingResponse.Rank; + NewRankingEl.Gap = TeamContestRankingResponse.Gap; + NewRankingEl.Time = TeamContestRankingResponse.Time; + Contest.ContestRanking.Add(NewRankingEl); + Contest.SortContestRanking(); + } + } + } +} + +void UDTFluxDataStorage::UpdateStageRanking(const FDTFluxStageRankingResponse& StageRankingResponse) +{ + // UE_LOG(LogDTFluxAPI, Log, TEXT("In DataStorage::UdpateStageRanking")); + // UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdate Contest%02d and Stage%02d"), + // StageRankingResponse.ContestID, StageRankingResponse.StageID); + for(auto& Contest: Contests) + { + if(Contest.Id == StageRankingResponse.ContestID) + { + // UE_LOG(LogDTFluxAPI, Log, TEXT("Found Contest::%02d "),Contest.Id); + if(Contest.Stages.IsEmpty()) + { + // UE_LOG(LogDTFluxAPI, Log, TEXT("WTF Contest::%02d HAS NO STAGES ???"),Contest.Id); + } + for(auto& Stage: Contest.Stages) + { + + // UE_LOG(LogDTFluxAPI, Log, TEXT("Current Stage is Stage::%02d "),Stage.Id); + if(Stage.Id == StageRankingResponse.StageID) + { + // UE_LOG(LogDTFluxAPI, Log, TEXT("Found Stage::%02d "),Stage.Id); + // Cleaning StageRanking + Stage.StageRanking.Empty(); + for(auto& StageRanking: StageRankingResponse.Datas ) + { + FDTFluxStageRanking NewStageRanking; + NewStageRanking.TimeRun = StageRanking.TimeRun; + NewStageRanking.TimeStart = StageRanking.TimeStart; + NewStageRanking.TimeTransition = StageRanking.TimeTransition; + NewStageRanking.TimeSwim = StageRanking.TimeSwim; + NewStageRanking.Bib = StageRanking.Bib; + NewStageRanking.Gap = StageRanking.Gap; + NewStageRanking.Rank = StageRanking.Rank; + Stage.StageRanking.Add(NewStageRanking); + Stage.SortStageRanking(); + // UE_LOG(LogDTFluxAPI, Log, + // TEXT("Testing -> Stage %02d::%s in Contest %02d::%s, has now %02d Rankings\n"), + // Stage.Id, *Stage.Name, Contest.Id, *Contest.Name, Stage.StageRanking.Num()); + } + } + } + } + } +} void UDTFluxDataStorage::AddSplitSensorResult(FDTFluxSplitSensorItemResponse Response) { @@ -329,3 +456,17 @@ void UDTFluxDataStorage::ChangeCurrentContest() } } + +const FString UDTFluxDataStorage::GetConcurrentFormatedName(int Bib, bool Truncate, int MaxSize) +{ + { + for(const auto& Participant : GetParticipants()) + { + if(Participant.Bib == Bib) + { + return Participant.GetParticipantFormatedName(Truncate, MaxSize); + } + } + return ""; + }; +} diff --git a/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp b/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp index 721ab3f..db6f67c 100644 --- a/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp +++ b/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp @@ -3,9 +3,26 @@ #include "DTFluxModel/DTFluxModel.h" -void FDTFluxSplit::Dump() const +bool FDTFluxParticipant::IsTeam() const { - UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxStage DUMP] Split ID : %i, Name:%s"), Id, *Name); + return (Person2.FirstName != ""); +} + +void FDTFluxParticipant::Dump() const{ + FString EliteStr = "NO"; + if(Elite) + { + EliteStr = "YES"; + } + UE_LOG(LogDTFluxAPI, Log, TEXT("PARTICIPANT with bib: %03d"), Bib); + UE_LOG(LogDTFluxAPI, Log, TEXT("Fullname : %s %s"), *Person1.FirstName, *Person1.LastName); + if(IsTeam()) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Teamate : %s %s"), *Person2.FirstName, *Person2.LastName); + UE_LOG(LogDTFluxAPI, Log, TEXT("Team name : %s"), *Team); + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Club : %s, Category : %s, IsElite : %s, Status : %s"), + *Club, *Category, *EliteStr, *UEnum::GetValueAsString(Status)); } @@ -35,51 +52,8 @@ bool FDTFluxStage::SetEndTime(const FDateTime& ContestDate, const FString& TimeS return true; } -bool FDTFluxStage::UpdateStageRanking(TArray> Data) -{ - return true; -} - -bool FDTFluxStage::AddSplit(TArray> SplitData) -{ - return true; -} - -void FDTFluxStage::Dump() const -{ - UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxStage DUMP] Id : %i, Name : %s"), Id, *Name); - UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxStage DUMP] StartTime : %s, EndTime : %s"), *StartTime.ToString(), *EndTime.ToString()); - UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxStage DUMP] Splits [")); - for(const auto& Split : Splits) - { - Split.Dump(); - } - UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxStage DUMP] ]")); - - -} - -bool FDTFluxContest::AddStage( const TArray> StagesData, TArray Splits) -{ - for (const auto& StageData : StagesData) - { - FDTFluxStage Stage; - Stage.Id = StageData->AsObject()->GetIntegerField(TEXT("id")); - Stage.Name = StageData->AsObject()->GetStringField(TEXT("name")); - FString StartTime = StageData->AsObject()->GetStringField(TEXT("startTime")); - FString EndTime = StageData->AsObject()->GetStringField(TEXT("endTime")); - Stage.SetStartTime(Date, StartTime); - Stage.SetEndTime(Date, EndTime); - Stage.Splits = Splits; - Stages.Add(Stage); - Stage.Dump(); - } - return true; -} - bool FDTFluxContest::SetDate(const FString& StringDate) { - TArray Tokens; StringDate.ParseIntoArray(Tokens, TEXT("-")); if(Tokens.Num() != 3) diff --git a/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModelResponse.cpp b/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModelResponse.cpp new file mode 100644 index 0000000..e17fe44 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModelResponse.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxModel/DTFluxModelResponse.h" diff --git a/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp b/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp index 39b34f8..14a1e3c 100644 --- a/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp +++ b/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp @@ -2,88 +2,17 @@ #include "DTFluxSubsystem/DTFluxSubsystem.h" #include "DTFluxProjectSettings/DTFluxProjectSettings.h" -#include "DTFluxWebSocket/DTFluxWebsocketServer.h" - #include "DTFluxModel/DTFluxModel.h" -#include "HttpServerModule.h" -#include "HttpRouteHandle.h" #include "DTFluxAPILog.h" #include "DTFluxDataStorage/DTFluxDataStorage.h" -#include "IHttpRouter.h" -#include "HttpModule.h" #include "JsonObjectConverter.h" -#include "Interfaces/IHttpResponse.h" -// DEPRECATED : Now in WS -FString FDTFluxSubsystemAPISettings::GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings) -{ - if(Settings) - { - FString RaceDataEndpoint = - FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("race-datas")); - UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Race Data -> %s"), *RaceDataEndpoint); - return RaceDataEndpoint; - } - return FString(""); -} -// DEPRECATED : Now in WS -FString FDTFluxSubsystemAPISettings::GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId) -{ - if(Settings) - { - FString Ranking = *Settings->ProxyEndpoints.FindKey("ranking"); - const TCHAR* ContestIDTmpl = *FString("{:ContestID}"); - const TCHAR* ContestIDValue = *FString(TEXT("%i"),ContestId); - FString ContestRanking = Ranking.Replace(ContestIDTmpl, ContestIDValue ); - FString ContestRankingEndpoint = Settings->GetProxyBaseEndpoint() + ContestRanking; - UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Contest Ranking -> %s"), *ContestRankingEndpoint); - return ContestRankingEndpoint; - } - return FString(""); -} -// DEPRECATED : Now in WS -FString FDTFluxSubsystemAPISettings::GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, - const int StageId) -{ - if(Settings) - { - FString StageRanking = GetContestRankingEndpoint(Settings, ContestId); - StageRanking = FString::Printf(TEXT("%s/stage/%i/"), *StageRanking, StageId); - UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking -> %s"), *StageRanking); - return StageRanking; - } - return FString(""); -} -// DEPRECATED : Now in WS -FString FDTFluxSubsystemAPISettings::GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings, - const int ContestId, const int StageId, const FString SplitName) -{ - if (Settings){ - FString StageRanking = GetStageRankingEndpoint(Settings, ContestId, StageId); - StageRanking = FString::Printf(TEXT("%s?splitname=%s"), *StageRanking, *SplitName); - UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking with Splitname -> %s"), *StageRanking); - return StageRanking; - } - return FString(""); -} +#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h" -// DEPRECATED : Now in WS -FString FDTFluxSubsystemAPISettings::GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings) -{ - if(Settings) - { - FString TeamsEndpoint = - FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("teams")); - UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Teams -> %s"), *TeamsEndpoint ); - return TeamsEndpoint; - } - return FString(""); -} /**** * DTFlux subsystem ****/ - void UDTFluxSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); @@ -98,12 +27,69 @@ void UDTFluxSubsystem::Initialize(FSubsystemCollectionBase& Collection) WsClient->Connect(SubSettings.WebsocketAddress, SubSettings.WebsocketPort); DataStorage = NewObject(); + // DataStorage->InitDatastorage(); + + + FDateTime Now = FDateTime::Now(); FDateTime Send1Min = Now + FTimespan::FromMinutes(1); UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer timeSpan Duration : %s"), *Send1Min.ToString()); - // SetTimerEvent( Send1Min ); - + // UWorld* World = nullptr; + // TIndirectArray WorldCtx = GEngine->GetWorldContexts(); + // for(const auto& Ctx : WorldCtx) + // { + // EWorldType::Type Type = Ctx.WorldType.GetValue(); + // switch(Type) + // { + // case EWorldType::None: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is None ")); + // break; + // + // case EWorldType::Editor: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is EDITOR ")); + // break; + // + // case EWorldType::Game: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is GAME ")); + // break; + // + // case EWorldType::GamePreview : + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is GamePreview ")); + // break; + // + // case EWorldType::EditorPreview: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is EditorPreview ")); + // break; + // + // case EWorldType::Inactive: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is Inactive ")); + // break; + // + // case EWorldType::PIE: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is PIE ")); + // break; + // + // case EWorldType::GameRPC: + // UE_LOG(LogDTFluxAPI, Log, TEXT("Ctx world is GameRPC ")); + // break; + // + // default: + // break; + // } + // } + // if(World) + // { + // UE_LOG(LogDTFluxAPI, Log, TEXT("World IS NOT NULL")); + // + // World->GetTimerManager().SetTimer( + // TestTimerHandle, this, &UDTFluxSubsystem::TestTimers, 1.0f, true); + // } + // else + // { + // UE_LOG(LogDTFluxAPI, Log, TEXT("World IS NULL:-D")); + // } + // WsServer Event binding } @@ -111,14 +97,22 @@ void UDTFluxSubsystem::Deinitialize() { if(WsClient) { - UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient not null")); + if (WsClient->Close()) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient is closed")); + } + UE_LOG(LogDTFluxAPI, Error, TEXT("WsClient can not be closed")); } - else - UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient has been GC'ed")); + UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient has been GC'ed")); Super::Deinitialize(); } +void UDTFluxSubsystem::InitDataStorage() +{ + +} + bool UDTFluxSubsystem::ReloadSubsystem() { return Reconnect(); @@ -127,6 +121,8 @@ bool UDTFluxSubsystem::ReloadSubsystem() bool UDTFluxSubsystem::Reconnect() { bool Result = WsClient->Close(); + DataStorageRaceDataInit = false; + DataStorageTeamListInit = false; if(!WsClient->IsConnected()) return WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort); return false; @@ -189,18 +185,7 @@ void UDTFluxSubsystem::Tick(float DeltaTime) } } -// TODO: IMPLEMENT THIS METHOD -void UDTFluxSubsystem::WsSplitSensorReceivedInternal() -{ -} -// TODO: IMPLEMENT THIS METHOD -void UDTFluxSubsystem::WsTeamUpdateReceivedInternal() -{ -} -// TODO: IMPLEMENT THIS METHOD -void UDTFluxSubsystem::WsStatusUpdateReceivedInternal() -{ -} + void UDTFluxSubsystem::RequestRaceDatas() { @@ -215,6 +200,7 @@ void UDTFluxSubsystem::RequestTeamList() void UDTFluxSubsystem::RequestContestRanking(const int ContestId) { const FString Request = FString::Printf(TEXT("{\"path\": \"contest-ranking\", \"contestID\" : %i}"), ContestId); + UE_LOG(LogDTFluxAPI, Log, TEXT("Sending %s to server"), *Request); WsClient->SendMessage(Request); } @@ -247,8 +233,6 @@ void UDTFluxSubsystem::UpdateTeam() { } - - void UDTFluxSubsystem::UpdateContestRanking(const int ContestID) { RequestContestRanking(ContestID); @@ -266,116 +250,6 @@ void UDTFluxSubsystem::UpdateStageRanking(const int ContestID, const int StageID } } - - - -EDTFluxResponseType UDTFluxSubsystem::FindResponseType(const FString& MessageReceived) -{ - EDTFluxResponseType ResponseType = UnknownResponse; - TSharedPtr JsonValue; - - // Create a reader pointer to read the json data - TSharedRef> Reader = TJsonReaderFactory<>::Create(MessageReceived); - if (FJsonSerializer::Deserialize(Reader, JsonValue)) - { - // Get the value of the json object by field name - TSharedPtr Json = JsonValue->AsObject(); - //Test - FString Type = Json->GetStringField(TEXT("type")); - if(Type == "") - { - // UE_LOG(LogDTFluxAPI, Log, TEXT("Type tupe does not exist")); - - return EDTFluxResponseType::UnknownResponse; - } - if(Type.Contains("race-datas")) - { - // TODO : check if object data are valid - FDTFluxRaceDataResponse RaceDataResponse; - if(!FJsonObjectConverter::JsonObjectToUStruct - (Json.ToSharedRef(), &RaceDataResponse)) - { - UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"race-data\" object"), *MessageReceived); - return EDTFluxResponseType::UnknownResponse; - } - UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s is valid race-data object"), *MessageReceived); - ProcessRaceDataResponse(RaceDataResponse); - return EDTFluxResponseType::RaceData; - } - if(Type.Contains("constest-ranking")) - { - FDTFluxContestRankingResponse ContestRankingResponse; - if(!FJsonObjectConverter::JsonObjectToUStruct - (Json.ToSharedRef(), &ContestRankingResponse)) - { - UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"contest-ranking\" object"), *MessageReceived); - } - // TODO : check if object data are valid - UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s is valid \"contest-ranking\" object"), *MessageReceived); - ProcessContestRankingResponse(ContestRankingResponse); - return EDTFluxResponseType::ContestRanking; - } - if(Type.Contains("stage-ranking")) - { - FDTFluxStageRankingResponse StageRankingResponse; - if(!FJsonObjectConverter::JsonObjectToUStruct - (Json.ToSharedRef(), &StageRankingResponse)) - { - UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"stage-ranking\" object"), *MessageReceived); - } - UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s is valid \"stage-ranking\" object"), *MessageReceived); - if(StageRankingResponse.SplitID == -1) - { - ProcessSplitRankingResponse(StageRankingResponse); - return EDTFluxResponseType::SplitRanking; - - } - ProcessStageRankingResponse(StageRankingResponse); - return EDTFluxResponseType::StageRanking; - } - if(Type.Contains("team-list")) - { - FDTFluxTeamListResponse TeamListResponse; - if( !FJsonObjectConverter::JsonObjectToUStruct - (Json.ToSharedRef(), &TeamListResponse)) - { - UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-list object"), *MessageReceived) - return EDTFluxResponseType::UnknownResponse; - } - UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-list data")); - ProcessTeamListResponse(TeamListResponse); - // TODO : check if object data are valid - return EDTFluxResponseType::TeamList; - } - if(Type.Contains("team-update")) - { - // TODO : check if object data are valid - return EDTFluxResponseType::TeamUpdate; - } - if(Type.Contains("split-sensor")) - { - // TODO : check if object data are valid - FDTFluxSplitSensorResponse SplitSensorResponse; - if( !FJsonObjectConverter::JsonObjectToUStruct - (Json.ToSharedRef(), &SplitSensorResponse)) - { - UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid split-sensor data"), *MessageReceived) - return EDTFluxResponseType::UnknownResponse; - } - UE_LOG(LogDTFluxAPI, Log, TEXT("Received split-sensor data")); - // send the array to DataStorage; - return EDTFluxResponseType::SplitSensor; - } - if(Type.Contains("status-update")) - { - // TODO : check if object data are valid - return EDTFluxResponseType::StatusUpdate; - } - } - - return ResponseType; -} - /*** * Timer handling ***/ @@ -391,6 +265,9 @@ void UDTFluxSubsystem::SetTimerEvent(const FDateTime& When) { FTimespan TimeSpan = FDateTime::Now() - When; UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer timeSpan Duration : %s"), *TimeSpan.GetDuration().ToString()); + FOnTimer NewTimer; + // NewTimer.BindUFunction() + // AddTimer(When, ) } @@ -407,47 +284,169 @@ bool UDTFluxSubsystem::AddTimer(FDateTime Time, FOnTimer NewTimer) void UDTFluxSubsystem::WsConnected() { - OnWsConnected.Broadcast(); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = EDTFluxResponseType::WsConnected; + Event.RawData = "Connected"; + if(WsClient->IsConnected()) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Initializing DataStorage")); + UpdateRaceData(); + } + OnWsEvent.Broadcast(Event); UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Connected")); + } void UDTFluxSubsystem::WsReceivedMessage( const FString& MessageReceived) { - OnWsIncomingData.Broadcast(MessageReceived); - // UE_LOG(LogDTFluxAPI, Log, TEXT("Ws ReceivedMessage %s"), *MessageReceived); - // Find Data Object Type - EDTFluxResponseType Type = FindResponseType(MessageReceived); - switch(Type) + FDTFluxWsResponseEvent Event; + Event.WsResponseType = UnknownResponse; + Event.RawData = MessageReceived; + + TSharedPtr JsonValue; + TSharedRef> Reader = TJsonReaderFactory<>::Create(MessageReceived); + if (FJsonSerializer::Deserialize(Reader, JsonValue)) { - case EDTFluxResponseType::TeamList: - break; - case EDTFluxResponseType::RaceData: - break; - case EDTFluxResponseType::ContestRanking: - break; - case EDTFluxResponseType::StageRanking: - break; - case EDTFluxResponseType::SplitRanking: - break; - case EDTFluxResponseType::TeamUpdate: - break; - default: - break; + TSharedPtr Json = JsonValue->AsObject(); + FString Type = Json->GetStringField(TEXT("type")); + if(Type.Contains("race-datas")) + { + FDTFluxRaceDataResponse RaceDataResponse; + if(!FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &RaceDataResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"race-data\" object"), *MessageReceived); + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s is valid race-data object"), *MessageReceived); + ProcessRaceDataResponse(RaceDataResponse); + if(!DataStorageRaceDataInit) + { + DataStorageRaceDataInit = true; + RequestTeamList(); + } + Event.WsResponseType = RaceData; + } + if(Type.Contains("contest-ranking")) + { + FDTFluxContestRankingResponse ContestRankingResponse; + if(!FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &ContestRankingResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"contest-ranking\" object"), *MessageReceived); + } + ProcessContestRankingResponse(ContestRankingResponse); + Event.WsResponseType = ContestRanking; + + } + if(Type.Contains("stage-ranking")) + { + FDTFluxStageRankingResponse StageRankingResponse; + if(!FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &StageRankingResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"stage-ranking\" object"), *MessageReceived); + } + UE_LOG(LogDTFluxAPI, Log, TEXT("\"stage-ranking\" object received")); + if(StageRankingResponse.SplitID == -1) + { + ProcessStageRankingResponse(StageRankingResponse); + Event.WsResponseType = StageRanking; + } + ProcessSplitRankingResponse(StageRankingResponse); + Event.WsResponseType = SplitRanking; + + } + if(Type.Contains("team-list")) + { + FDTFluxTeamListResponse TeamListResponse; + if( !FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &TeamListResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-list object"), *MessageReceived) + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-list data")); + ProcessTeamListResponse(TeamListResponse); + if(!DataStorageTeamListInit) + { + DataStorageTeamListInit = true; + // Initialize contest-rankings + for(const auto& Contest: DataStorage->Contests) + { + RequestContestRanking(Contest.Id); + // Initialize stage-rankings + for(const auto Stage : Contest.Stages) + { + RequestStageRanking(Contest.Id, Stage.Id); + } + } + } + Event.WsResponseType = TeamList; + } + if(Type.Contains("team-update")) + { + FDTFluxTeamUpdateResponse TeamUpdateResponse; + if( !FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &TeamUpdateResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-update object"), *MessageReceived) + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-update data")); + ProcessTeamUpdateResponse(TeamUpdateResponse); + Event.WsResponseType = TeamUpdate; + } + if(Type.Contains("split-sensor")) + { + FDTFluxSplitSensorResponse SplitSensorResponse; + if( !FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &SplitSensorResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid split-sensor data"), *MessageReceived) + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Received split-sensor data")); + Event.WsResponseType = SplitSensor; + + } + if(Type.Contains("status-update")) + { + FDTFluxStatusUpdateResponse StatusUpdateResponse; + if( !FJsonObjectConverter::JsonObjectToUStruct + (Json.ToSharedRef(), &StatusUpdateResponse)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid status-update data"), *MessageReceived) + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Received status-update data %s"), *MessageReceived); + ProcessStatusUpdateResponse(StatusUpdateResponse); + Event.WsResponseType = StatusUpdate; + + } } - // Let datastorage Know that we received something - // DataStorage->UpdateDataStorage(MessageReceived); + OnWsEvent.Broadcast(Event); } void UDTFluxSubsystem::WsConnectionClosed(const FString& Reason) { - OnWsClosed.Broadcast(Reason); - UE_LOG(LogDTFluxAPI, Log, TEXT("Ws ConnectionClosed with reason %s"), *Reason); + UE_LOG(LogDTFluxAPI, Log, TEXT("Ws ConnectionClosed with reason %s trying to reconnect"), *Reason); + + if(!WsClient->IsConnected()){} + WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = WsClosed; + Event.RawData = Reason; + OnWsEvent.Broadcast(Event); } void UDTFluxSubsystem::WsConnectionError(const FString& Error) { - OnWsError.Broadcast(Error); - UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Error %s"), *Error); + UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Error %s trying to reconnect"), *Error); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = WsError; + Event.RawData = Error; + OnWsEvent.Broadcast(Event); + bool Result = WsClient->Close(); + DataStorageRaceDataInit = false; + DataStorageTeamListInit = false; + if(!WsClient->IsConnected()){} + WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort); } bool UDTFluxSubsystem::IsConnected() const @@ -459,14 +458,12 @@ void UDTFluxSubsystem::ProcessTeamListResponse(const FDTFluxTeamListResponse& Te { for( const auto& TeamListItemResponse : TeamListResponse.Datas) { - UE_LOG(LogDTFluxAPI, Log, TEXT("Sending Participant %s with Bib %d to DataStorage"), *TeamListItemResponse.LastName, TeamListItemResponse.Bib); - DataStorage->AddOrUpdateParticipant(TeamListItemResponse); } - for(auto& Contest : DataStorage->Contests) - { - Contest.DumpParticipant(); - } + // for(auto& Contest : DataStorage->Contests) + // { + // Contest.DumpParticipant(); + // } // UE_LOG(LogDTFluxAPI, Log, TEXT("New Particpant list Size %d"), DataStorage->GetParticipants().Num()) @@ -478,60 +475,77 @@ void UDTFluxSubsystem::ProcessRaceDataResponse(const FDTFluxRaceDataResponse& Da { DataStorage->AddOrUpdateContest(ContestResponse); } - - UE_LOG(LogDTFluxAPI, Log, TEXT("New Contest Size %d"), DataStorage->Contests.Num()) + FDTFluxWsResponseEvent Event; + Event.WsResponseType = RaceData; + Event.RawData = "race-data"; + OnWsEvent.Broadcast(Event); + // UE_LOG(LogDTFluxAPI, Log, TEXT("New Contest Size %d"), DataStorage->Contests.Num()) } - void UDTFluxSubsystem::ProcessContestRankingResponse(const FDTFluxContestRankingResponse& ContestRankingResponse) { - TArray NewRankings; - - for(const auto& TeamContestRankingResponse : ContestRankingResponse.Datas) - { - FDTFluxContestRanking NewRankingEl; - NewRankingEl.Participant = DataStorage->GetParticipant(ContestRankingResponse.ContestID, TeamContestRankingResponse.Bib); - NewRankingEl.Rank = TeamContestRankingResponse.Rank; - NewRankingEl.Gap = TeamContestRankingResponse.Gap; - NewRankingEl.Time = TeamContestRankingResponse.Time; - NewRankings.Add(NewRankingEl); - } + DataStorage->UpdateContestRanking(ContestRankingResponse); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = ContestRanking; + Event.RawData = "contest-ranking"; + OnWsEvent.Broadcast(Event); } void UDTFluxSubsystem::ProcessStageRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse) { + DataStorage->UpdateStageRanking(StageRankingResponse); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = StageRanking; + Event.RawData = "stage-ranking"; + OnWsEvent.Broadcast(Event); } void UDTFluxSubsystem::ProcessSplitRankingResponse(const FDTFluxStageRankingResponse& SplitRankingResponse) { + DataStorage->UpdateSplitRanking(SplitRankingResponse); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = SplitRanking; + Event.RawData = "split-ranking"; + OnWsEvent.Broadcast(Event); } void UDTFluxSubsystem::ProcessTeamUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse) { + UE_LOG(LogDTFluxAPI, Log, TEXT("team-update received in c++")); + for(auto& TeamListRespItem: TeamUpdateResponse.Datas) + { + DataStorage->AddOrUpdateParticipant(TeamListRespItem); + } + FDTFluxWsResponseEvent Event; + Event.WsResponseType = TeamUpdate; + Event.RawData = "team-update"; + OnWsEvent.Broadcast(Event); } -void UDTFluxSubsystem::ProcessStatusUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse) +void UDTFluxSubsystem::ProcessStatusUpdateResponse(const FDTFluxStatusUpdateResponse& StatusUpdateResponse) { - + UE_LOG(LogDTFluxAPI, Log, TEXT("Processing status-update data")); + DataStorage->UpdateParticipantStatus(StatusUpdateResponse); + FDTFluxWsResponseEvent Event; + Event.WsResponseType = StatusUpdate; + Event.RawData = "status-update"; + OnWsEvent.Broadcast(Event); } void UDTFluxSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorResponse& SplitSensorResponse) { -} + // -TSharedPtr UDTFluxSubsystem::GetData(EDTFluxResponseType Type, const FString& Message) -{ - TSharedPtr JsonValue; - TSharedPtr Object; - - // Create a reader pointer to read the json data - TSharedRef> Reader = TJsonReaderFactory<>::Create(Message); - if (FJsonSerializer::Deserialize(Reader, JsonValue)) + FDTFluxWsResponseEvent Event; + Event.WsResponseType = SplitSensor; + Event.RawData = "split-sensor"; + OnWsEvent.Broadcast(Event); + // determine if SplitSensorResponse come from a finisher spot + if(DataStorage->IsFinisherSplit(SplitSensorResponse)) { - + FDTFluxFinisher Finisher = DataStorage->GetFinisherStatus(SplitSensorResponse); + OnFinisher.Broadcast(Finisher); } - return Object; } - diff --git a/Source/DTFluxAPI/Private/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.cpp b/Source/DTFluxAPI/Private/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.cpp new file mode 100644 index 0000000..6ddf833 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.cpp @@ -0,0 +1,70 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h" +#include "DTFluxAPILog.h" + +// DEPRECATED : Now in WS +FString FDTFluxSubsystemAPISettings::GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings) +{ + if(Settings) + { + FString RaceDataEndpoint = + FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("race-datas")); + UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Race Data -> %s"), *RaceDataEndpoint); + return RaceDataEndpoint; + } + return FString(""); +} +// DEPRECATED : Now in WS +FString FDTFluxSubsystemAPISettings::GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId) +{ + if(Settings) + { + FString Ranking = *Settings->ProxyEndpoints.FindKey("ranking"); + const TCHAR* ContestIDTmpl = *FString("{:ContestID}"); + const TCHAR* ContestIDValue = *FString(TEXT("%i"),ContestId); + FString ContestRanking = Ranking.Replace(ContestIDTmpl, ContestIDValue ); + FString ContestRankingEndpoint = Settings->GetProxyBaseEndpoint() + ContestRanking; + UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Contest Ranking -> %s"), *ContestRankingEndpoint); + return ContestRankingEndpoint; + } + return FString(""); +} +// DEPRECATED : Now in WS +FString FDTFluxSubsystemAPISettings::GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, + const int StageId) +{ + if(Settings) + { + FString StageRanking = GetContestRankingEndpoint(Settings, ContestId); + StageRanking = FString::Printf(TEXT("%s/stage/%i/"), *StageRanking, StageId); + UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking -> %s"), *StageRanking); + return StageRanking; + } + return FString(""); +} +// DEPRECATED : Now in WS +FString FDTFluxSubsystemAPISettings::GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings, + const int ContestId, const int StageId, const FString SplitName) +{ + if (Settings){ + FString StageRanking = GetStageRankingEndpoint(Settings, ContestId, StageId); + StageRanking = FString::Printf(TEXT("%s?splitname=%s"), *StageRanking, *SplitName); + UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking with Splitname -> %s"), *StageRanking); + return StageRanking; + } + return FString(""); +} + +// DEPRECATED : Now in WS +FString FDTFluxSubsystemAPISettings::GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings) +{ + if(Settings) + { + FString TeamsEndpoint = + FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("teams")); + UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Teams -> %s"), *TeamsEndpoint ); + return TeamsEndpoint; + } + return FString(""); +} diff --git a/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxEnums.cpp b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxEnums.cpp new file mode 100644 index 0000000..14c68b7 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxEnums.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxUtils/DTFluxEnums.h" diff --git a/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxHttpServerStruct.cpp b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxHttpServerStruct.cpp deleted file mode 100644 index 4bcaf86..0000000 --- a/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxHttpServerStruct.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023 Dexter.Wan. All Rights Reserved. -// EMail: 45141961@qq.com - -#include "DTFluxUtils/DTFluxHttpServerStruct.h" - -void UDTFluxHttpServerBPFn::BreakParams(const FDTFluxHttpServerParams& HttpServerParams, TMap& Params) -{ - Params = HttpServerParams.Params; -} - -void UDTFluxHttpServerBPFn::FindParam(const FDTFluxHttpServerParams& HttpServerParams, const FString& Key, FString& Param) -{ - Param.Empty(); - if ( const FString * pParam = HttpServerParams.Params.Find(Key) ) - { - Param = *pParam; - } -} - -void UDTFluxHttpServerBPFn::BreakHeaders(const FDTFluxHttpServerHeaders& HttpServerHeaders, TMap& Headers) -{ - Headers = HttpServerHeaders.Headers; -} - -void UDTFluxHttpServerBPFn::BreakBody(const FDTFluxHttpServerBody& HttpBody, TArray RawBody) -{ - RawBody = HttpBody.ReqBody; -} - -void UDTFluxHttpServerBPFn::FindHeader(const FDTFluxHttpServerHeaders& HttpServerHeaders, const FString& Key, FString& Header) -{ - Header.Empty(); - if ( const FString * pHeader = HttpServerHeaders.Headers.Find(Key) ) - { - Header = *pHeader; - } -} - diff --git a/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxUtils.cpp b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxUtils.cpp new file mode 100644 index 0000000..e20cef9 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxUtils.cpp @@ -0,0 +1,7 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxUtils/DTFluxUtils.h" + +#include "DTFluxModel/DTFluxModel.h" + diff --git a/Source/DTFluxAPI/Public/DTFluxCountDown/DTFluxCountDownComponent.h b/Source/DTFluxAPI/Public/DTFluxCountDown/DTFluxCountDownComponent.h new file mode 100644 index 0000000..a76b635 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxCountDown/DTFluxCountDownComponent.h @@ -0,0 +1,68 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "AvaText3DComponent.h" +#include "Components/ActorComponent.h" +#include "DTFluxCountDownComponent.generated.h" + + + + + +UCLASS(ClassGroup=(DTFlux), meta=(BlueprintSpawnableComponent)) +class DTFLUXAPI_API UDTFluxCountDownComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + // Sets default values for this component's properties + UDTFluxCountDownComponent(); + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter") + FDateTime GoTime; + UPROPERTY(BlueprintSetter=SetDuration, Category="DTFlux|Counter") + int32 Duration; + + +protected: + // Called when the game starts + virtual void BeginPlay() override; + UPROPERTY() + UAvaText3DComponent* TextRef; + FTimerHandle WaitingTimer; + FTimerHandle ContDownTimer; + int64 InternalDuration; + bool IsWaiting; + bool IsCounting; + + + +public: + + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter") + FString EndString; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter") + float WaitingRate = 1.0f; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter") + float CountDownRate = 1.0f; + + // Called every frame + virtual void TickComponent(float DeltaTime, ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction) override; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Counter") + void SetGoTime(FDateTime NewGoTime); + UFUNCTION(BlueprintCallable, Category="DTFlux|Counter") + void SetDuration(int32 NewDuration); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Counter") + void SetTarget(UAvaText3DComponent* TextComponent); + + UFUNCTION() + void CountUpTimerFn(); + + UFUNCTION() + void WaitingTimerFn(); + +}; diff --git a/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h b/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h index 9e7580c..60eb742 100644 --- a/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h +++ b/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h @@ -10,14 +10,33 @@ /** * */ - - struct FDTFluxStageRanking; struct FDTFluxTeam; struct FDTFluxParticipant; struct FDTFluxStage; struct FDTFluxContest; +UENUM(BlueprintType, Category="DTFlux|DataStorage") +// ReSharper disable once IdentifierTypo +enum EDTFluxDataStorageEventType : uint8 +{ + UnknownEvent = 0 UMETA(DisplayName="ParticipantUpdateEvent"), + ParticipantCreateEvent = 1 UMETA(DisplayName="ParticipantUpdateEvent"), + ParticipantUpdateEvent = 2 UMETA(DisplayName="ParticipantUpdateEvent"), + ParticipantDeleteEvent = 3 UMETA(DisplayName="ParticipantUpdateEvent"), + ParticipantStatusUpdateEvent = 4 UMETA(DisplayName="ParticipantUpdateEvent"), + RaceDataCreateEvent = 5 UMETA(DisplayName="ParticipantUpdateEvent"), + RaceDataUpdateEvent = 6 UMETA(DisplayName="ParticipantUpdateEvent"), + RaceDataDeleteEvent = 7 UMETA(DisplayName="ParticipantUpdateEvent"), + ContestRankingUpdate = 8 UMETA(DisplayName="ParticipantUpdateEvent"), + StageRankingUpdate = 9 UMETA(DisplayName="ParticipantUpdateEvent"), + SplitRankingUpdate = 10 UMETA(DisplayName="ParticipantUpdateEvent"), +}; + + + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDataStorageUpdated, FString, What); + UCLASS(BlueprintType, Category="DTFlux|Datastorage") class DTFLUXAPI_API UDTFluxDataStorage : public UObject { @@ -26,10 +45,8 @@ class DTFLUXAPI_API UDTFluxDataStorage : public UObject friend FDTFluxStage; public: - UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage") - bool UpdateDataStorage(const FString JsonPayload); - - UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage") + + UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage") TArray Contests; UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage") int CurrentStageId = 0; @@ -43,7 +60,15 @@ public: return CurrentContestId; } return -1; - }; + } + + void UpdateSplitRanking(const FDTFluxStageRankingResponse& SplitRankingResponse); + // void UpdateParticipant(const FDTFluxTeamUpdateResponse& TeamUpdateResponse); + void UpdateParticipantStatus(const FDTFluxStatusUpdateResponse& StatusUpdateResponse); + bool IsFinisherSplit(const FDTFluxSplitSensorResponse& SplitSensorResponse); + FDTFluxFinisher GetFinisherStatus(const FDTFluxSplitSensorResponse& SplitSensorResponse); + + UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage") bool GetContest(FDTFluxContest& OutContest, const int& ContestId); UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage") @@ -53,7 +78,7 @@ public: UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage") TArray GetParticipants(const int ContestId = -1); UFUNCTION() - FDTFluxParticipant GetParticipant(const int ContestID, const int ParticipantBib); + void GetParticipant(const int ContestID, const int ParticipantBib, FDTFluxParticipant& OutParticipant); UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage") TArray GetStageRanking(const int ContestId, const int StageId); @@ -61,6 +86,10 @@ public: void AddOrUpdateContest(const FDTFluxContestResponse& ContestResponse); UFUNCTION(BlueprintType, Category="DTFlux|Datastorage") void AddOrUpdateParticipant(const FDTFluxTeamListItemResponse& TeamListItemResponse); + UFUNCTION(BlueprintType, Category="DTFlux|Datastorage") + void UpdateContestRanking(const FDTFluxContestRankingResponse& InContestRanking); + UFUNCTION(BlueprintType, Category="DTFlux|Datastorage") + void UpdateStageRanking(const FDTFluxStageRankingResponse& StageRankingResponse); UFUNCTION(BlueprintCallable, Category="DTFlux") bool IsInitialized() @@ -92,5 +121,8 @@ public: void GoToNextStage(); UFUNCTION(BlueprintCallable, Category="DTFlux") void ChangeCurrentContest(); + UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage") + const FString GetConcurrentFormatedName( int Bib, bool Truncate = true, int MaxSize = 20); + }; diff --git a/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h b/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h index 6d013e7..7051763 100644 --- a/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h +++ b/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h @@ -4,6 +4,8 @@ #include "CoreMinimal.h" #include "DTFluxAPILog.h" +#include "DTFluxModelResponse.h" +#include "DTFluxUtils/DTFluxEnums.h" #include "UObject/Object.h" #include "DTFluxModel.generated.h" @@ -25,7 +27,6 @@ public: FString FunctionLine2 = TEXT(""); }; - USTRUCT(BlueprintType, Category="DTFlux|Model") struct DTFLUXAPI_API FDTFluxParticipant { @@ -44,28 +45,111 @@ public: UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") bool Elite; UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") - FString Status; - bool IsTeam() + TEnumAsByte Status; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") + FString Team; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") + int LastSplitId = 0; + bool IsTeam() const; + void Dump() const; + FString GetParticipantFormatedName(bool Truncate = false, int MaxSize = 20) const { - return (Person2.FirstName != ""); + FString ParticipantName; + if(Truncate) + { + if(IsTeam()) + { + //Concatenate the team name; + if(Team.Len() > MaxSize - 3) + { + return Team.Left(MaxSize - 3).Append(TEXT("...")); + } + return Team; + } + if(Person1.FirstName.Contains("-") ) + { + FString Formated = ""; + //Compound Firstname + TArray Out; + Person1.FirstName.ParseIntoArray(Out,TEXT("-"),true); + for(const auto& Str : Out) + { + Formated.Append(Str.Left(1).ToUpper()).Append("."); + } + // TODO : Camel Case handling for LastName + Formated.Append(" ").Append(*Person1.LastName); + UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"), + *Formated, Formated.Len(), MaxSize); + if(Formated.Len() >= MaxSize) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated); + + return Formated.Left(MaxSize - 3).Append("..."); + } + return Formated; + } + if(Person1.FirstName.Contains(" ")) + { + FString Formated = ""; + //Compound Firstname + TArray Out; + Person1.FirstName.ParseIntoArray(Out,TEXT(" "),true); + for(const auto& Str : Out) + { + Formated.Append(Str.Left(1).ToUpper()).Append("."); + } + // TODO : Camel Case handling for LastName + Formated.Append(" ").Append(*Person1.LastName); + UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"), + *Formated, Formated.Len(), MaxSize); + if(Formated.Len() >= MaxSize) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated); + return Formated.Left(MaxSize - 3).Append("..."); + } + return Formated; + } + FString Formated = Person1.FirstName.Left(1).Append(". "); + Formated.Append(Person1.LastName); + UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is not compound. Formated Name %s length %02d MAX Size : %02d"), + *Formated, Formated.Len(), MaxSize); + if(Formated.Len() >= MaxSize) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated); + return Formated.Left(MaxSize - 3).Append("..."); + } + return Formated; + } + else + { + if(!IsTeam()) + { + return FString::Printf(TEXT("%s %s"), *Person1.FirstName, *Person2.LastName); + } + return Team; + } } }; - - USTRUCT(BlueprintType, Category="DTFlux|Model") struct DTFLUXAPI_API FDTFluxContestRanking { GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") - FDTFluxParticipant Participant; + int Bib; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") int Rank; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") FString Gap; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") FString Time; + void Dump() + { + UE_LOG(LogDTFluxAPI, Log, + TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "), + Rank, Bib, *Gap, *Time ); + }; }; USTRUCT(BlueprintType, Category="DTFlux|Model") @@ -74,7 +158,7 @@ struct DTFLUXAPI_API FDTFluxStageRanking GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") - FDTFluxParticipant Participant; + int Bib; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") int Rank; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") @@ -87,28 +171,36 @@ public: FString TimeRun; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") FString TimeStart; + + void Dump() const + { + UE_LOG(LogDTFluxAPI, Log, TEXT("RANKING : %02d. Participant bib %d %s %s %s %s %s"), + Rank, Bib, *Gap, *TimeSwim, + *TimeTransition, *TimeRun, *TimeStart); + } }; USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxSplitGapItem +struct DTFLUXAPI_API FDTFluxSplitRanking { GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") - FDTFluxParticipant Participant; + int Bib; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") - int Gap; -}; -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxSplitGap -{ - GENERATED_BODY() -public: + FString Gap; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") - TArray SplitGapItems; + FString Time; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") + int Rank; + void Dump() const + { + UE_LOG(LogDTFluxAPI, Log, TEXT("SplitGapItem")) + // Participant.Dump(); + UE_LOG(LogDTFluxAPI, Log, TEXT("Bib %02d Gap %s"), Bib, *Gap); + } }; - USTRUCT(BlueprintType, Category="DTFlux|Model") struct DTFLUXAPI_API FDTFluxSplit { @@ -119,9 +211,46 @@ public: UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model") FString Name; UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") - TArray SplitGaps; + TArray SplitGaps; - void Dump() const; + void Dump() const + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Split %02d::%s *****\n"), Id, *Name); + for(const auto& SplitGapItem : SplitGaps) + { + SplitGapItem.Dump(); + } + } + + void InsertOrReplace(const FDTFluxStageRankingResponseItem& SplitRankingItemResp) + { + FDTFluxSplitRanking NewSplitGapItem; + NewSplitGapItem.Bib = SplitRankingItemResp.Bib; + NewSplitGapItem.Gap = SplitRankingItemResp.Gap; + if(SplitGaps.IsEmpty()) + { + SplitGaps.Add(NewSplitGapItem); + return; + } + bool Update = true; + int Idx = 0; + for(auto& SplitGapItem : SplitGaps) + { + if(SplitGapItem.Bib == SplitRankingItemResp.Bib) + { + Update = false; + } + Idx++; + } + if(Update) + { + if(SplitGaps.IsValidIndex(Idx)) + { + SplitGaps.RemoveAt(Idx); + } + } + SplitGaps.Add(NewSplitGapItem); + }; }; USTRUCT(BlueprintType, Category="DTFlux|Model") @@ -143,10 +272,32 @@ public: TArray StageRanking; bool SetStartTime(const FDateTime& ContestDate, const FString& TimeString); bool SetEndTime(const FDateTime& ContestDate, const FString& TimeString); - bool UpdateStageRanking(TArray> StageRankingData); - bool AddSplit(TArray> SplitData); - void Dump() const; + void Dump() const + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Stage %02d::%s"), Id, *Name); + UE_LOG(LogDTFluxAPI, Log, TEXT("Starts at %s and is supposed to finnish at %s"), + *StartTime.ToString(), *EndTime.ToString()); + UE_LOG(LogDTFluxAPI, Log, TEXT("Splits : \n")); + for(const auto& StageRankingEl : StageRanking) + { + StageRankingEl.Dump(); + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Splits : \n")); + for(const auto& Split : Splits) + { + Split.Dump(); + } + }; + void SortStageRanking() + { + StageRanking.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B) + { + if(A.Rank == 0 || B.Rank == 0) + return true; + return A.Rank > B.Rank; + }); + }; protected: }; @@ -165,8 +316,9 @@ public: TArray Stages; UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") FDateTime Date; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") + TArray ContestRanking; - bool AddStage( const TArray> StagesData, const TArray Splits); bool SetDate(const FString& StringDate); void AddParticipant(const FDTFluxParticipant& Participant) { @@ -182,6 +334,9 @@ public: { if(P.Bib == Participant.Bib) { + UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, OLD : %s %s new %s %s in Contest%02d"), + Index, *P.Person1.FirstName, *P.Person1.LastName, + *Participant.Person1.FirstName, *Participant.Person1.LastName, Id); ToUpdate = P; Update = true; break; @@ -193,11 +348,17 @@ public: } if(Update) { - Participants.RemoveAt(Index); + if(Participants.IsValidIndex(Index)) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, REMOVED : %s %s in Contest%02d"), + Index, + *Participants[Index].Person1.FirstName, *Participants[Index].Person1.LastName, Id); + Participants.RemoveAt(Index); + } + } Participants.Add(Participant); }; - bool GetParticipant(const int Bib, FDTFluxParticipant& OutParticipant) { for (auto& Participant : Participants) @@ -210,7 +371,6 @@ public: } return false; } - void DumpParticipant() { int Num = 0; @@ -222,269 +382,89 @@ public: UE_LOG(LogDTFluxAPI, Log, TEXT("DUMP Participant : In Contest with ID %d there are %d Participant(s)"), Id, Num); }; -}; - - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxSplitSensorItemResponse -{ - GENERATED_BODY() - UPROPERTY() - int Bib; - UPROPERTY() - FString Type = "split-sensor-item"; - UPROPERTY() - int ContestID; - UPROPERTY() - int StageID; - UPROPERTY() - int SplitID; - UPROPERTY() - FString Time; -}; - - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxSplitSensorResponse -{ - GENERATED_BODY() - UPROPERTY() - FString Type = "split-sensor"; - UPROPERTY() - TArray Datas; -}; - - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxTeamListItemResponse -{ - GENERATED_BODY() -public: - UPROPERTY() - FString Type = "team-list-item"; - UPROPERTY() - int ContestId; - UPROPERTY() - int Bib; - UPROPERTY() - FString FirstName; - UPROPERTY() - FString LastName; - UPROPERTY() - FString FirstName2 = ""; - UPROPERTY() - FString LastName2 = ""; - UPROPERTY() - FString Team = ""; - UPROPERTY() - FString Gender; - UPROPERTY() - bool Elite; - UPROPERTY() - FString Category; - UPROPERTY() - FString Status; - UPROPERTY() - FString Club; -}; - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxTeamListResponse -{ - GENERATED_BODY() -public: - UPROPERTY() - FString Type = "team-list"; - UPROPERTY() - TArray Datas; - -}; - - -USTRUCT() -struct DTFLUXAPI_API FDTFluxTeamUpdateResponse -{ - GENERATED_BODY() - UPROPERTY() - FString Type = "team-update"; - UPROPERTY() - TArray Datas; - -}; - - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FStageResponse -{ - GENERATED_BODY() -public: - UPROPERTY() - FString Type = "stage-response-data"; - UPROPERTY() - int Id; - UPROPERTY() - FString Name; - UPROPERTY() - FString StartTime; - UPROPERTY() - FString EndTime; -}; - - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FSplitResponse -{ - GENERATED_BODY() -public: - UPROPERTY() - FString Type = "split-response-data"; - UPROPERTY() - int Id; - UPROPERTY() - FString Name; -}; - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxContestResponse -{ - GENERATED_BODY() -public: - UPROPERTY() - FString Type = "contest"; - UPROPERTY() - int Id; - UPROPERTY() - FString Name; - UPROPERTY() - FString Date; - UPROPERTY() - TArray Stages; - UPROPERTY() - TArray Splits; -}; - -USTRUCT() -struct DTFLUXAPI_API FDTFluxTeamContestRankingResponse -{ - GENERATED_BODY() - -public: - UPROPERTY() - FString Type = "team-contest-ranking"; - UPROPERTY() - int Bib; - UPROPERTY() - int Rank; - UPROPERTY() - FString Time; - UPROPERTY(); - FString Gap; -}; - -USTRUCT() -struct DTFLUXAPI_API FDTFluxContestRankingResponse -{ - GENERATED_BODY() - -public: - UPROPERTY() - FString Type = "contest-ranking"; - UPROPERTY() - int ContestID; - UPROPERTY() - TArray Datas; -}; - - -USTRUCT() -struct DTFLUXAPI_API FDTFluxTeamStageRankingResponse -{ - GENERATED_BODY() - -public: - UPROPERTY() - FString Type = "team-stage-ranking"; - UPROPERTY() - int Bib; - UPROPERTY() - int Rank; - UPROPERTY() - FString Time; - UPROPERTY(); - FString Gap; - UPROPERTY() - FString TimeSwim; - UPROPERTY(); - FString TimeTransition; - UPROPERTY() - FString TimeRun; - UPROPERTY(); - FString TimeStart; -}; - -USTRUCT() -struct DTFLUXAPI_API FDTFluxStageRankingResponse -{ - GENERATED_BODY() - -public: - UPROPERTY() - FString Type = "stage-ranking"; - UPROPERTY() - int ContestID; - UPROPERTY() - int StageID; - UPROPERTY() - int SplitID; - UPROPERTY() - TArray Datas; -}; - -USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXAPI_API FDTFluxRaceDataResponse -{ - GENERATED_BODY() -public: - - UPROPERTY() - FString Type = "race-datas"; - UPROPERTY() - TArray Datas; -}; - -USTRUCT() -struct DTFLUXAPI_API FDTFluxStatusTeamUpdateResponse -{ - GENERATED_BODY() - UPROPERTY() - FString Type = "status-team-update"; - UPROPERTY() - int Bib; - UPROPERTY() - FString Status; -}; - -USTRUCT() -struct DTFLUXAPI_API FDTFluxStatusUpdateResponse -{ - GENERATED_BODY() - UPROPERTY() - FString Type = "status-update"; - TArray Datas; - -}; - -UCLASS(BlueprintType, Category="DTFlux|Model|Helpers") -class DTFLUXAPI_API UDTFluxModelHelper : public UBlueprintFunctionLibrary -{ - GENERATED_BODY() - -public: - UFUNCTION(BlueprintCallable, Category="DTFlux|Model") - static bool IsParticipantATeam(const FDTFluxParticipant& Participant) + bool AddContestRanking(const FDTFluxContestRanking& NewRanking) { - return Participant.Person2.FirstName != ""; + bool Update = false; + if(ContestRanking.IsEmpty()) + { + ContestRanking.Add(NewRanking); + return true; + } + int Idx = 0; + for( auto& Ranking : ContestRanking) + { + if(NewRanking.Bib == Ranking.Bib) + { + // we need to update a ranking + Update = true; + break; + } + Idx++; + } + if(Update) + { + ContestRanking.RemoveAt(Idx); + ContestRanking.Insert(NewRanking, Idx); + UE_LOG(LogDTFluxAPI, Log, + TEXT("Inserting %d with rank %d in Contest with ID %d"), + NewRanking.Bib, NewRanking.Rank, Id ); + return true; + } + else + { + ContestRanking.Add(NewRanking); + return true; + } + return false; } + void Dump() + { + UE_LOG(LogDTFluxAPI, Log, TEXT("CONTEST DUMP BEGIN *****%s::%02d *****\n"), *Name, Id); + UE_LOG(LogDTFluxAPI, Log, TEXT("Date : %s"), *Date.ToString()); + UE_LOG(LogDTFluxAPI, Log, TEXT("PARTICIPANTS : \n")); + DumpParticipant(); + for(auto& Stage: Stages) + { + Stage.Dump(); + } + for(auto& ContestRankingEl: ContestRanking) + { + ContestRankingEl.Dump(); + } + UE_LOG(LogDTFluxAPI, Log, TEXT("CONTEST DUMP END *****%s::%02d *****\n"), *Name, Id); + } + void SortContestRanking() + { + ContestRanking.Sort([](const FDTFluxContestRanking& A, const FDTFluxContestRanking& B) + { + if(A.Rank == 0 || B.Rank == 0) + return true; + return A.Rank > B.Rank; + }); + }; +}; + +USTRUCT(BlueprintType, Category="FDTFlux|Model") +struct FDTFluxFinisher +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model") + TEnumAsByte Type; + UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model") + FDTFluxParticipant Participant; + UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model") + FDTFluxStageRanking CurrentRanking; }; +USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events") +struct FDTFluxWsResponseEvent +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events") + TEnumAsByte WsResponseType; + UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events") + FString RawData; +}; diff --git a/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModelResponse.h b/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModelResponse.h new file mode 100644 index 0000000..1977d63 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModelResponse.h @@ -0,0 +1,290 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "DTFluxModelResponse.generated.h" + +/** + * + */ + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FSplitResponse +{ + GENERATED_BODY() +public: + UPROPERTY() + FString Type = "split-response-data"; + UPROPERTY() + int Id; + UPROPERTY() + FString Name; +}; + + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FStageResponse +{ + GENERATED_BODY() +public: + UPROPERTY() + FString Type = "stage-response-data"; + UPROPERTY() + int Id; + UPROPERTY() + FString Name; + UPROPERTY() + FString StartTime; + UPROPERTY() + FString EndTime; + +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxContestResponse +{ + GENERATED_BODY() +public: + UPROPERTY() + FString Type = "contest"; + UPROPERTY() + int Id; + UPROPERTY() + FString Name; + UPROPERTY() + FString Date; + UPROPERTY() + TArray Stages; + UPROPERTY() + TArray Splits; +}; + +USTRUCT() +struct DTFLUXAPI_API FDTFluxContestRankingResponseItem +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString Type = "team-contest-ranking"; + UPROPERTY() + int Bib; + UPROPERTY() + int Rank; + UPROPERTY() + FString Time; + UPROPERTY(); + FString Gap; + UPROPERTY(); + FString SpeedSwimAverage; + UPROPERTY(); + FString SpeedRunningAverage; + UPROPERTY(); + FString SpeedTotalAverage; +}; + +USTRUCT() +struct DTFLUXAPI_API FDTFluxContestRankingResponse +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString Type = "contest-ranking"; + UPROPERTY() + int ContestID; + UPROPERTY() + TArray Datas; +}; + +USTRUCT() +struct DTFLUXAPI_API FDTFluxStageRankingResponseItem +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString Type = "team-stage-ranking"; + UPROPERTY() + int Bib; + UPROPERTY() + int Rank; + UPROPERTY() + FString Time; + UPROPERTY(); + FString Gap; + UPROPERTY() + FString TimeSwim; + UPROPERTY(); + FString TimeTransition; + UPROPERTY() + FString TimeRun; + UPROPERTY(); + FString TimeStart; + UPROPERTY() + FString SpeedSwim; + UPROPERTY() + FString SpeedRunning; + UPROPERTY() + FString SpeedTotal; +}; + + +USTRUCT() +struct DTFLUXAPI_API FDTFluxStageRankingResponse +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString Type = "stage-ranking"; + UPROPERTY() + int ContestID; + UPROPERTY() + int StageID; + UPROPERTY() + int SplitID = -1; + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Datas; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxRaceDataResponse +{ + GENERATED_BODY() +public: + + UPROPERTY() + // ReSharper disable once StringLiteralTypo + FString Type = "race-datas"; + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Datas; +}; + +USTRUCT() +struct DTFLUXAPI_API FDTFluxStatusTeamUpdateResponse +{ + GENERATED_BODY() + UPROPERTY() + FString Type = "status-team-update"; + UPROPERTY() + int Bib; + UPROPERTY() + int Status; +}; + +USTRUCT() +struct DTFLUXAPI_API FDTFluxStatusUpdateResponse +{ + GENERATED_BODY() + UPROPERTY() + FString Type = "status-update"; + UPROPERTY() + // ReSharper disable once IdentifierTypo + FDTFluxStatusTeamUpdateResponse Datas; + +}; + + + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxSplitSensorItemResponse +{ + GENERATED_BODY() + UPROPERTY() + int Bib; + UPROPERTY() + FString Type = "split-sensor-item"; + UPROPERTY() + int ContestID; + UPROPERTY() + int StageID; + UPROPERTY() + int SplitID; + UPROPERTY() + FString Time = "-"; + UPROPERTY() + FString Gap = "-"; + UPROPERTY() + int Rank; +}; + + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxSplitSensorResponse +{ + GENERATED_BODY() + UPROPERTY() + FString Type = "split-sensor"; + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Datas; +}; + + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxTeamListItemResponse +{ + GENERATED_BODY() +public: + UPROPERTY() + FString Type = "team-list-item"; + UPROPERTY() + int ContestId; + UPROPERTY() + int Bib; + UPROPERTY() + FString FirstName; + UPROPERTY() + FString LastName; + UPROPERTY() + FString FirstName2 = ""; + UPROPERTY() + FString LastName2 = ""; + UPROPERTY() + FString Team = ""; + UPROPERTY() + FString Gender; + UPROPERTY() + FString Gender2; + UPROPERTY() + bool Elite; + UPROPERTY() + FString Category; + UPROPERTY() + int Status; + UPROPERTY() + FString Club; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxTeamListResponse +{ + GENERATED_BODY() +public: + UPROPERTY() + FString Type = "team-list"; + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Datas; + +}; + + +USTRUCT() +struct DTFLUXAPI_API FDTFluxTeamUpdateResponse +{ + GENERATED_BODY() + UPROPERTY() + FString Type = "team-update"; + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Datas; + +}; + + diff --git a/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h b/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h index 1318518..2fb8294 100644 --- a/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h +++ b/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h @@ -4,112 +4,35 @@ #include "CoreMinimal.h" #include "Runtime/Engine/Public/Subsystems/EngineSubsystem.h" -#include "DTFluxWebSocket/DTFluxWebsocketServer.h" +// #include "DTFluxWebSocket/DTFluxWebsocketServer.h" +// +// #include "HttpServerRequest.h" +// #include "HttpResultCallback.h" +// #include "HttpRouteHandle.h" +// #include -#include "HttpServerRequest.h" -#include "HttpResultCallback.h" -#include "HttpRouteHandle.h" -#include - -#include "DTFluxUtils/DTFluxHttpServerStruct.h" #include "DTFluxAPILog.h" #include "DTFluxDataStorage/DTFluxDataStorage.h" #include "DTFluxModel/DTFluxModel.h" +#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h" #include "DTFluxWebSocket/DTFluxWebsocketClient.h" #include "DTFluxSubsystem.generated.h" - class UDTFluxDataStorage; class UDTFluxProjectSettings; -class IHttpRouter; - -class IHttpRequest; -class IHttpResponse; - -class FHttpModule; -typedef TSharedPtr FHttpRequestPtr; -typedef TSharedPtr FHttpResponsePtr; - -UENUM(BlueprintType, Category="DTFlux|Server") -enum EDTFluxResponseErrorCode -{ - Unknown_Error UMETA(DisplayName="Unknown Error"), - InvalidBody_Error UMETA(DisplayName="Invalid Body"), - InvalidRequest_Error UMETA(DisplayName="Invalid Request"), - Internal_Error UMETA(DisplayName="Internal Server Error") -}; - -UENUM(BlueprintType, Category="DTFlux|Subsystem") -enum EDTFluxResponseType: uint8 -{ - UnknownResponse = 0 UMETA(DisplayName="UnknownResponse"), - RaceData = 1 UMETA(DisplayName="RaceData"), - ContestRanking = 2 UMETA(DisplayName="ContestRanking"), - StageRanking = 3 UMETA(DisplayName="StageRanking"), - SplitRanking = 4 UMETA(DisplayName="SplitRanking"), - TeamList = 5 UMETA(DisplayName="TeamList"), - TeamUpdate = 6 UMETA(DisplayName="TeamUpdate"), - SplitSensor = 7 UMETA(DisplayName="SplitSensor"), - StatusUpdate = 8 UMETA(DisplayName="StatusUpdate"), -}; - -USTRUCT(BlueprintType, Category="DTFlux|Server") -struct FDTFluxResponseBody -{ - GENERATED_BODY() - - UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) - FString Error; - - UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) - FString Success; - -FString Deserialize() - { - FString JSONObject; - JSONObject += TEXT("{"); - JSONObject += TEXT("\"error\":\""); - JSONObject += Error.IsEmpty() ? TEXT("") : Error; - JSONObject += TEXT("\",\"success\":\""); - JSONObject += Success.IsEmpty() ? TEXT("") : Success; - JSONObject += TEXT("\"}"); - UE_LOG(LogDTFluxAPI, Log, TEXT("JSONObject : %s"), *JSONObject); - return JSONObject; - } -}; -USTRUCT() -struct FDTFluxSubsystemAPISettings -{ - GENERATED_BODY() -public: - FString WebsocketAddress = "ws://localhost"; - int WebsocketPort = 3000; - FString ProxyAddress = "http://localhost"; - int ProxyPort = 80; - //TODO : Maybe we must make a dedicated struct with enum to make endpoints more clean. - TMap ProxyEndpoints; - static FString GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings); - static FString GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId); - static FString GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId); - static FString GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId, const FString SplitName); - static FString GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings); -private: - FString GetProxyBaseEndpoint() const - { - return FString::Printf(TEXT("%s:%i"), *ProxyAddress, ProxyPort); - }; -}; -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWsConnected); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWsIncomingData, FString, JsonData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWsError, FString, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWsClosed, FString, Reason); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTimerTriggered); DECLARE_DYNAMIC_DELEGATE_OneParam(FOnTimer, FString, TimerName); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWsEvent, FDTFluxWsResponseEvent, WsResponseEvent); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxFinisher, Finisher); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestBegin, int, ContestId); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageBegin, int, ContestId, int, StageId); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTimesUp, int, ContestId, int, StageId); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRestTimeBegin, int, ContestId, int, StageId); /** * DTFlux API Subsystem * @@ -145,12 +68,6 @@ private: protected: - UFUNCTION() - void WsSplitSensorReceivedInternal(); - UFUNCTION() - void WsTeamUpdateReceivedInternal(); - UFUNCTION() - void WsStatusUpdateReceivedInternal(); UFUNCTION() void RequestRaceDatas(); UFUNCTION() @@ -167,6 +84,8 @@ protected: void BroadcastTimerEvent(); UPROPERTY() TMap Timer; + bool DataStorageRaceDataInit = false; + bool DataStorageTeamListInit = false; public: /** Implement this for initialization of instances of the system */ @@ -174,22 +93,29 @@ public: /** Implement this for deinitialization of instances of the system */ virtual void Deinitialize() override; - + void InitDataStorage(); + void LoadConfig(const UDTFluxProjectSettings* Settings); + UFUNCTION(BlueprintCallable, Category="DTFluxAPI | Subsytem") bool ReloadSubsystem(); - void LoadConfig(const UDTFluxProjectSettings* Settings); - UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") FOnTimerTriggered OnTimerTriggered; UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") - FOnWsIncomingData OnWsIncomingData; + FOnWsEvent OnWsEvent; UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") - FOnWsConnected OnWsConnected; + FOnFinisher OnFinisher; UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") - FOnWsError OnWsError; + FOnContestBegin OnContestBegin; UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") - FOnWsClosed OnWsClosed; + FOnStageBegin OnStageBegin; + UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") + FOnTimesUp OnTimesUp; + UPROPERTY(BlueprintAssignable, Category="DTFlux|Events") + FOnRestTimeBegin FOnRestTimeBegin; + + // UPROPERTY(BlueprintReadWrite, Category="DTFlux|Subsystem|Websocket") + // int ReconnectTimeout = 60; //seconds UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|WebSocket") bool Reconnect(); @@ -229,14 +155,10 @@ public: UFUNCTION() void ProcessTeamUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse); UFUNCTION() - void ProcessStatusUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse); + void ProcessStatusUpdateResponse(const FDTFluxStatusUpdateResponse& StatusUpdateResponse); UFUNCTION() void ProcessSplitSensor(const FDTFluxSplitSensorResponse& SplitSensorResponse); - TSharedPtr GetData(EDTFluxResponseType Type, const FString& Message); - - UFUNCTION() - EDTFluxResponseType FindResponseType(const FString& MessageReceived); UFUNCTION() void WsConnected(); @@ -251,6 +173,13 @@ public: bool IsConnected() const; + + FTimerHandle TestTimerHandle; + + void TestTimers() + { + UE_LOG(LogDTFluxAPI, Log, TEXT("IT WORKS !!!!")); + } }; diff --git a/Source/DTFluxAPI/Public/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h b/Source/DTFluxAPI/Public/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h new file mode 100644 index 0000000..407a98c --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "DTFluxSubsystemAPISettings.generated.h" + +/** + * + */ +USTRUCT() +struct FDTFluxSubsystemAPISettings +{ + GENERATED_BODY() +public: + FString WebsocketAddress = "ws://localhost"; + int WebsocketPort = 3000; + FString ProxyAddress = "http://localhost"; + int ProxyPort = 80; + //TODO : Maybe we must make a dedicated struct with enum to make endpoints more clean. + TMap ProxyEndpoints; + + static FString GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings); + static FString GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId); + static FString GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId); + static FString GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId, const FString SplitName); + static FString GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings); + +private: + FString GetProxyBaseEndpoint() const + { + return FString::Printf(TEXT("%s:%i"), *ProxyAddress, ProxyPort); + }; +}; diff --git a/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxEnums.h b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxEnums.h new file mode 100644 index 0000000..f9e1368 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxEnums.h @@ -0,0 +1,55 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "DTFluxEnums.generated.h" + +/** + * + */ +UENUM() +enum EDTFluxParticipantStatusType : uint8 +{ + Normal = 0 UMETA(DisplayName="Normal"), + OutOfRace = 1 UMETA(DisplayName="HorsCourse"), + DSQ = 2 UMETA(DisplayName="Disqualifié"), + DNF = 3 UMETA(DisplayName="Abandon"), + DNS = 4 UMETA(DisplayName="NonPartant"), + NotLinedUp = 5 UMETA(DisplayName="NonPresentAuDépart"), +}; + +UENUM() +enum EDTFluxFinisherType : uint8 +{ + Winner = 0 UMETA(DisplayName="Winner"), + Spotter = 1 UMETA(DisplayName="Spotter"), + Finish = 2 UMETA(DisplayName="Finish"), +}; + +UENUM(BlueprintType, Category="DTFlux|Server") +enum EDTFluxResponseErrorCode +{ + Unknown_Error UMETA(DisplayName="Unknown Error"), + InvalidBody_Error UMETA(DisplayName="Invalid Body"), + InvalidRequest_Error UMETA(DisplayName="Invalid Request"), + Internal_Error UMETA(DisplayName="Internal Server Error") +}; + +UENUM(BlueprintType, Category="DTFlux|Subsystem") +enum EDTFluxResponseType: uint8 +{ + UnknownResponse = 0 UMETA(DisplayName="UnknownResponse"), + RaceData = 1 UMETA(DisplayName="RaceData"), + ContestRanking = 2 UMETA(DisplayName="ContestRanking"), + StageRanking = 3 UMETA(DisplayName="StageRanking"), + SplitRanking = 4 UMETA(DisplayName="SplitRanking"), + TeamList = 5 UMETA(DisplayName="TeamList"), + TeamUpdate = 6 UMETA(DisplayName="TeamUpdate"), + SplitSensor = 7 UMETA(DisplayName="SplitSensor"), + StatusUpdate = 8 UMETA(DisplayName="StatusUpdate"), + WsConnected = 9 UMETA(DisplayName="WsConnected"), + WsClosed = 10 UMETA(DisplayName="WsClosed"), + WsError = 11 UMETA(DisplayName="WsError"), +}; \ No newline at end of file diff --git a/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxHttpServerStruct.h b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxHttpServerStruct.h deleted file mode 100644 index d21770c..0000000 --- a/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxHttpServerStruct.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2023 Dexter.Wan. All Rights Reserved. -// EMail: 45141961@qq.com - -#pragma once - -#include "CoreMinimal.h" -#include "Kismet/BlueprintFunctionLibrary.h" -#include "DTFluxHttpServerStruct.generated.h" - -UENUM(BlueprintType) -enum EDTFluxHttpServerVerbs : uint8 -{ - HTTP_GET, - HTTP_POST, - HTTP_PUT, - HTTP_PATCH, - HTTP_DELETE, -}; - -// USTRUCT(BlueprintType, meta=(DisplayName="DT Http Server Headers", HasNativeBreak = "DTHttpServer.DTHttpServerBPLib.BreakHeaders")) -USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakHeaders")) -struct FDTFluxHttpServerHeaders -{ - GENERATED_BODY() - TMap Headers; -}; - -USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakBody")) -struct FDTFluxHttpServerBody -{ - GENERATED_BODY() - TArray ReqBody; -}; - -// USTRUCT(BlueprintType, meta=(DisplayName="DTFlux Server Params", HasNativeBreak = "DTHttpServer.DTHttpServerBPLib.BreakParams")) -USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakParams")) -struct FDTFluxHttpServerParams -{ - GENERATED_BODY() - TMap Params; -}; - -UCLASS(NotBlueprintable, NotBlueprintType) -class DTFLUXAPI_API UDTFluxHttpServerBPFn : public UBlueprintFunctionLibrary -{ - GENERATED_BODY() - -public: - // Break DTFlux Http Server Params - UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Params"), Category = "DT Http Server|Params") - static void BreakParams(const FDTFluxHttpServerParams& HttpServerParams, TMap& Params); - - // Find DTFlux Http Server Params - UFUNCTION(BlueprintPure, meta = (DisplayName="Find Http Server Params"), Category = "DT Http Server|Params") - static void FindParam(const FDTFluxHttpServerParams& HttpServerParams, const FString & Key, FString & Param ); - - // Break DTFlux Http Server Headers - UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Headers"), Category = "DT Http Server|Headers") - static void BreakHeaders(const FDTFluxHttpServerHeaders& HttpServerHeaders, TMap& Headers); - - // Break DTFlux Request Body - UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Headers"), Category = "DT Http Server|Headers") - static void BreakBody(const FDTFluxHttpServerBody& HttpBody, TArray RawBody); - - // Find DTFlux Http Server Headers - UFUNCTION(BlueprintPure, meta = (DisplayName="Find Http Server Headers"), Category = "DT Http Server|Headers") - static void FindHeader(const FDTFluxHttpServerHeaders& HttpServerHeaders, const FString & Key, FString & Header ); -}; diff --git a/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxUtils.h b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxUtils.h new file mode 100644 index 0000000..7876a1f --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxUtils.h @@ -0,0 +1,24 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "DTFluxModel/DTFluxModel.h" +#include "UObject/Object.h" +#include "DTFluxUtils.generated.h" + +/** + * + */ +UCLASS(BlueprintType, Category="DTFlux|Model|Helpers") +class DTFLUXAPI_API UDTFluxModelHelper : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category="DTFlux|Model") + static bool IsParticipantATeam(const FDTFluxParticipant& Participant) + { + return Participant.Person2.FirstName != ""; + } +};