Agrarsense
ActorInformation.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
6#include "ActorInformation.h"
7
9
10#include "Engine/GameViewportClient.h"
11#include "Engine/World.h"
12#include "Engine.h"
13
14#include "Kismet/GameplayStatics.h"
15#include "GameFramework/Actor.h"
16#include "Containers/Map.h"
17#include "Misc/Guid.h"
18
20{
21 FString ID = "";
22 TWeakObjectPtr<AActor> Actor;
23 int32 Count = 1;
24};
25
26static TMap<FString, IdentifierData> ExistingIDs;
27
28FString IActorInformation::ValidateID(const FString& ActorID, TWeakObjectPtr<AActor> Actor)
29{
30 FString ActorIDLower = ActorID.ToLower();
31 FString FinalID = ActorIDLower;
32
33 // Check if the ID already exists
34 if (ExistingIDs.Contains(FinalID))
35 {
36 IdentifierData& Data = ExistingIDs[FinalID];
37
38 if (!Data.Actor.IsValid())
39 {
40 // Reuse the ID if the existing Actor no longer exists.
41 Data.Actor = Actor;
42 return FinalID;
43 }
44
45 // Append the count as a numerical suffix
46 FinalID = ActorIDLower + FString::Printf(TEXT("%d"), Data.Count);
47
48 while (ExistingIDs.Contains(FinalID))
49 {
50 // If the new ID exists, keep incrementing the count
51 Data.Count++;
52 FinalID = ActorIDLower + FString::Printf(TEXT("%d"), Data.Count);
53 }
54 }
55
56 // Add the final ID to the map
57 ExistingIDs.Add(FinalID, IdentifierData{ FinalID, Actor });
58
59 return FinalID;
60}
61
63{
64 return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphensLower);
65}
66
67void IActorInformation::SetAndValidateActorIDAndName(FString& ActorName, FString& ActorID, TWeakObjectPtr<AActor> Actor)
68{
69 if (!Actor.IsValid())
70 {
71#if WITH_EDITOR
72 UE_LOG(LogTemp, Warning, TEXT("IActorID.cpp: Invalid Actor!"));
73#endif
74 return;
75 }
76
77 if (ActorName.IsEmpty())
78 {
79 // If the given Actor name is empty, use Actor actual name or label as name
80 // this name is used for Unreal UI side only.
81 ActorName = Actor->GetActorNameOrLabel();
82 }
83
84 if (!ActorID.IsEmpty())
85 {
86 // Validate the ID, If same ID exists, ValidateID adds available number at the end: Actor1..Actor2..ActorN
87 ActorID = ValidateID(ActorID, Actor);
88 }
89 else
90 {
91 // If given Actor ID is empty, generate unique ID (long string)
92 ActorID = GenerateUniqueID();
93 }
94}
95
97{
98 if (!GEngine || !GEngine->GameViewport)
99 {
100 return false;
101 }
102
103 TArray<AActor*> ActorsWithInterface;
104 UWorld* World = GEngine->GameViewport->GetWorld();
105 UGameplayStatics::GetAllActorsWithInterface(World, UActorInformation::StaticClass(), ActorsWithInterface);
106
107 // All ID's are in lowercase.
108 FString IDLowered = ID.ToLower();
109
110 for (AActor* Actor : ActorsWithInterface)
111 {
112 FString ActorID = IActorInformation::Execute_GetActorID(Actor);
113 if (ActorID == IDLowered)
114 {
115 return Actor->Destroy();
116 }
117 }
118
119 return false;
120}
121
122AActor* IActorInformation::GetActorByID(const FString& ID)
123{
124 if (!GEngine || !GEngine->GameViewport)
125 {
126 return nullptr;
127 }
128
129 TArray<AActor*> ActorsWithInterface;
130 UWorld* World = GEngine->GameViewport->GetWorld();
131 UGameplayStatics::GetAllActorsWithInterface(World, UActorInformation::StaticClass(), ActorsWithInterface);
132
133 // All ID's are in lowercase.
134 FString IDLowered = ID.ToLower();
135
136 for (AActor* Actor : ActorsWithInterface)
137 {
138 if (Actor)
139 {
140 FString ActorID = IActorInformation::Execute_GetActorID(Actor);
141 if (ActorID == IDLowered)
142 {
143 return Actor;
144 }
145 }
146 }
147
148 return nullptr;
149}
150
151template<typename T>
153{
154 if (!GEngine || !GEngine->GameViewport)
155 {
156 return TArray<T*>();
157 }
158
159 TArray<AActor*> ActorsWithInterface;
160 UWorld* World = GEngine->GameViewport->GetWorld();
161 UGameplayStatics::GetAllActorsWithInterface(World, UActorInformation::StaticClass(), ActorsWithInterface);
162
163 // Convert the TArray<AActor*> to TArray<T*>
164 TArray<T*> ConvertedActors;
165 for (AActor* Actor : ActorsWithInterface)
166 {
167 T* TypedActor = Cast<T>(Actor);
168 if (TypedActor)
169 {
170 ConvertedActors.Add(TypedActor);
171 }
172 }
173
174 return ConvertedActors;
175}
176
178{
179 FString Message = "IDs: ";
180 int32 NumIds = ExistingIDs.Num();
181
182 // Iterate through the ExistingIDs map
183 int32 IdCounter = 0;
184 for (auto It = ExistingIDs.CreateIterator(); It; ++It)
185 {
186 if (It.Value().Actor.IsValid())
187 {
188 // Append the ID to the Message string
189 Message += FString::Printf(TEXT("%s"), *It.Key());
190 if (++IdCounter < NumIds)
191 {
192 // Add a comma if it's not the last value
193 Message += TEXT(", ");
194 }
195 }
196 else
197 {
198 // Remove entries with nullptr actors
199 It.RemoveCurrent();
200 }
201 }
202
203 SimulatorLog::Log(Message);
204}
205
206#if WITH_EDITOR
207void IActorInformation::ClearTMapEditor()
208{
209 ExistingIDs.Empty();
210}
211#endif
static TMap< FString, IdentifierData > ExistingIDs
static FString GenerateUniqueID()
static void PrintAllIds()
static TArray< T * > GetActorsWithInterface()
static FString ValidateID(const FString &ActorID, TWeakObjectPtr< AActor > Actor)
static bool DestroyActorByID(const FString &ID)
static AActor * GetActorByID(const FString &ID)
static void SetAndValidateActorIDAndName(FString &ActorName, FString &ActorID, TWeakObjectPtr< AActor > Actor)
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
TWeakObjectPtr< AActor > Actor