Agrarsense
OverlapSensor.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 "OverlapSensor.h"
13
14#include "Components/InstancedStaticMeshComponent.h"
15#include "Components/SkeletalMeshComponent.h"
16#include "Components/StaticMeshComponent.h"
17#include "Engine/SkeletalMesh.h"
18#include "Engine/StaticMesh.h"
19
20AOverlapSensor::AOverlapSensor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
21{
22 PrimaryActorTick.bCanEverTick = false;
23
24 BoundsComponent = CreateDefaultSubobject<UBoundsVisualizerComponent>(TEXT("BoundsComponent"));
25 BoundsComponent->SetupAttachment(RootComponent);
26 BoundsComponent->SetGenerateOverlapEvents(true);
27 BoundsComponent->SetVisibility(false);
28 BoundsComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
29}
30
32{
33 Parameters = InParameters;
34
35 Vehicle = Cast<AVehicle>(Parameters.OwningActor);
36
37 if (Vehicle)
38 {
39 VehicleName = IActorInformation::Execute_GetActorName(Vehicle);
40 VehicleID = IActorInformation::Execute_GetActorID(Vehicle);
41 }
42
43 UPrimitiveComponent* TriggerComponent = Cast<UPrimitiveComponent>(BoundsComponent);
44
45 if (!InParameters.AllChannels)
46 {
47 const int32 OverlapSensorIndex = 2;
48 const ECollisionChannel OverlapSensorChannel = UCollisionProfile::Get()->ConvertToCollisionChannel(false, OverlapSensorIndex);
49
50 BoundsComponent->SetCollisionObjectType(OverlapSensorChannel);
51 BoundsComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
52 BoundsComponent->SetCollisionResponseToChannel(OverlapSensorChannel, ECollisionResponse::ECR_Overlap);
53 }
54 else
55 {
56 // Set to use custom collision profile defined in DefaultEngine.ini
57 BoundsComponent->SetCollisionProfileName(TEXT("OverlapSensorAll"));
58 }
59
60 BoundsComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
61 BoundsComponent->SetGenerateOverlapEvents(true);
62 BoundsComponent->SetRelativeLocation(Parameters.RelativePosition);
63
64 if (RootComponent)
65 {
66 BoundsComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);
67 }
68
69 TriggerComponent = BoundsComponent;
70
71 if (TriggerComponent)
72 {
73 SetOverlapBounds(InParameters.Size);
74 //SetRootComponent(TriggerComponent);
76
77 ROSMessages::std_msgs::String StringMsg;
78 ROSMessage = MakeShared<ROSMessages::std_msgs::String>(StringMsg);
79
81 {
83 }
84 }
85
87 {
88 FVector Size = FVector(Parameters.Size.X, Parameters.Size.Y, Parameters.Size.Z);
89 BoundsComponent->SetRelativeScale3D(Size);
90 BoundsComponent->SetRelativeLocation(Parameters.RelativePosition);
91
92 if (RootComponent)
93 {
94 BoundsComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);
95 }
96 }
97
99 {
100 AttachToActor(Parameters.OwningActor, FAttachmentTransformRules::KeepWorldTransform);
101 }
102
104}
105
107{
108 Super::BeginPlay();
109
110 BoundsComponent->OnComponentBeginOverlap.AddDynamic(this, &AOverlapSensor::OnOverlapBegin);
111 BoundsComponent->OnComponentEndOverlap.AddDynamic(this, &AOverlapSensor::OnOverlapEnd);
112}
113
114void AOverlapSensor::EndPlay(const EEndPlayReason::Type EndPlayReason)
115{
116 Super::EndPlay(EndPlayReason);
117
118 BoundsComponent->OnComponentBeginOverlap.RemoveDynamic(this, &AOverlapSensor::OnOverlapBegin);
119 BoundsComponent->OnComponentEndOverlap.RemoveDynamic(this, &AOverlapSensor::OnOverlapEnd);
120}
121
122void AOverlapSensor::SetOverlapBounds(const FVector& NewSize)
123{
124 Parameters.Size = NewSize;
125
126 if (BoundsComponent)
127 {
128 FVector Size = FVector(Parameters.Size.X, Parameters.Size.Y , Parameters.Size.Z);
129 BoundsComponent->SetRelativeScale3D(Size);
130 }
131
132 FString ActorID = IActorInformation::Execute_GetActorID(this);
133
134 FString Msg = FString::Printf(TEXT("OverlapSensor with ID: %s changed overlap bounds to: %s"), *ActorID, *NewSize.ToString());
136}
137
139{
140 IsVisible = Visualize;
141
142 if (BoundsComponent)
143 {
144 BoundsComponent->SetVisibility(IsVisible);
145 }
146}
147
149{
151
152 if (BoundsComponent)
153 {
154 BoundsComponent->SetRelativeLocation(Parameters.RelativePosition);
155 }
156
157 FString Msg = FString::Printf(TEXT("OverlapSensor.cpp: Changed overlap relative position to: %s"), *Vector.ToString());
159}
160
161void AOverlapSensor::SetOverlapResponseToAllChannels(const ECollisionResponse Response)
162{
163 if (Response != ECollisionResponse::ECR_Overlap && Response != ECollisionResponse::ECR_Ignore)
164 {
165 // If Response is neither ECR_Overlap nor ECR_Ignore, return
166 return;
167 }
168
169 if (BoundsComponent)
170 {
171 BoundsComponent->SetCollisionResponseToAllChannels(Response);
172 }
173}
174
175void AOverlapSensor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
176{
177 if (IsValidOverlap(OtherActor, OtherComp))
178 {
179 BuildAndSendMessage("Overlap Begin", OtherActor, OtherComp, OtherBodyIndex);
180 }
181}
182
183void AOverlapSensor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
184{
185 if (IsValidOverlap(OtherActor, OtherComp))
186 {
187 BuildAndSendMessage("Overlap End", OtherActor, OtherComp, OtherBodyIndex);
188 }
189}
190
191bool AOverlapSensor::IsValidOverlap(const AActor* OtherActor, const UPrimitiveComponent* OtherComp) const
192{
193 if (!IsValid(OtherComp)) return false;
194 if (OtherActor && OtherActor->IsA<AOverlapSensor>()) return false;
195 if (OtherComp->IsA<UBoundsVisualizerComponent>()) return false;
196
197 // Only consider overlaps with these component types
198 return OtherComp->IsA<UInstancedStaticMeshComponent>() ||
199 OtherComp->IsA<UStaticMeshComponent>() ||
200 OtherComp->IsA<USkeletalMeshComponent>();
201}
202
203void AOverlapSensor::BuildAndSendMessage(const FString& Prefix, AActor* Actor, UPrimitiveComponent* Comp, int32 Index)
204{
205 if (!Actor) return;
206
207 const FString ID = TryGetID(Actor);
208
209 AActor* From = this;
210 if (AActor* VehicleActorPtr = GetParentActor())
211 {
212 From = VehicleActorPtr;
213 }
214
215 if (Vehicle == Actor)
216 {
217 // BP_Drone is implemented in a way that the Actor itself doesn't move,
218 // only its skeletal mesh component does. So ignore overlapping with the vehicle itself.
219 return;
220 }
221
222 FVector Location = Actor->GetActorLocation();
223
224 if (Comp)
225 {
226 if (const UInstancedStaticMeshComponent* ISM = Cast<UInstancedStaticMeshComponent>(Comp))
227 {
228 if (Index >= 0)
229 {
230 FTransform Xf;
231 if (ISM->GetInstanceTransform(Index, Xf, true))
232 {
233 // If ISM and valid index, use the instance's world transform for location
234 Location = Xf.GetLocation();
235 }
236 }
237 }
238 }
239
240 // Distance from sensor parent (vehicle) to the (instance) location
241 const float DistanceInMeters = FVector::Dist(From->GetActorLocation(), Location) / 100.0f;
242
243 const FString Phase = Prefix.Replace(TEXT(" "), TEXT("_"));
244 const FString ActorNm = Actor->GetName();
245 const FString CompCls = Comp ? Comp->GetClass()->GetName() : TEXT("none");
246
247 // Try to get the mesh name
248 FString MeshName;
249 if (const UInstancedStaticMeshComponent* ISM = Cast<UInstancedStaticMeshComponent>(Comp))
250 {
251 if (const UStaticMesh* SM = ISM->GetStaticMesh()) { MeshName = SM->GetName(); }
252 }
253 else if (const UStaticMeshComponent* SMC = Cast<UStaticMeshComponent>(Comp))
254 {
255 if (const UStaticMesh* SM = SMC->GetStaticMesh()) { MeshName = SM->GetName(); }
256 }
257 else if (const USkeletalMeshComponent* SKC = Cast<USkeletalMeshComponent>(Comp))
258 {
259 if (const USkeletalMesh* Skel = SKC->GetSkeletalMeshAsset()) { MeshName = Skel->GetName(); }
260 }
261
262 auto Quote = [](FString S)
263 {
264 S.ReplaceInline(TEXT("\""), TEXT("\\\"")); return FString::Printf(TEXT("%s"), *S);
265 };
266 const FString Timestamp = CreateTimeStampString();
267
268 const FString FullMessage = FString::Printf(
269 TEXT("phase=%s actor=%s id=%s distance_m=%.2f location=%.2f,%.2f,%.2f component=%s mesh=%s timestamp=%s"),
270 *Phase,
271 *Quote(ActorNm),
272 *Quote(ID),
273 DistanceInMeters,
274 Location.X, Location.Y, Location.Z,
275 *CompCls,
276 *Quote(MeshName),
277 *Quote(Timestamp));
278
279 UTopic* Topic = GetROSTopic();
280 if (ROSMessage.IsValid() && Topic && IsROSConnected())
281 {
282 ROSMessage->_Data = FullMessage;
283 Topic->Publish(ROSMessage);
284 }
285
286 WriteToLogFile(FullMessage);
287}
288
290{
291 if (!FileSavePath.IsEmpty())
292 {
293 return;
294 }
295
296 const FString DataLocation = UAgrarsensePaths::GetDataFolder();
297
298 // Default save path
299 FileSavePath = DataLocation + GetActorID_Implementation() + "/";
300
301 if (AVehicle* AttachedVehicle = IsAttachedToVehicle())
302 {
303 // If sensor is attached to vehicle, save data to vehicle folder
304 FileSavePath = DataLocation + AttachedVehicle->GetActorID_Implementation();
305 }
306}
307
309{
310 if (IsValid(LogFile))
311 {
312 // File has already been created, return
313 return;
314 }
315
317
318 FLogFileSettings Settings;
321 Settings.QueueLength = MAX_int32;
322 Settings.KeepFileOpen = false;
323 Settings.Timestamp = false;
324 Settings.OverrideFilePath = true;
325 Settings.FilePath = FileSavePath;
326
327 UE_LOG(LogTemp, Warning, TEXT("FileSavePath %s"), *FileSavePath);
328
329 LogFile = NewObject<ULogFile>(ULogFile::StaticClass());
330 if (IsValid(LogFile))
331 {
332 FString Prefix;
333 if (AVehicle* AttachedVehicle = IsAttachedToVehicle())
334 {
335 Prefix = AttachedVehicle->GetActorID_Implementation();
336 }
337 FString FileName = Prefix.IsEmpty() ? TEXT("overlap") : Prefix + TEXT("_overlap");
338
339 LogFile->Create(FileName, Settings);
340 }
341}
342
343FString AOverlapSensor::TryGetID(AActor* Actor)
344{
345 FString ID;
346
347 if (Actor && Actor->GetClass()->ImplementsInterface(UActorInformation::StaticClass()))
348 {
349 if (Actor->Implements<UActorInformation>())
350 {
351 ID = IActorInformation::Execute_GetActorID(Actor);
352 }
353 }
354
355 return ID;
356}
void OnOverlapEnd(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex)
void CreateDataSavePath() override
void SetOverlapRelativePosition(const FVector &Vector)
TSharedPtr< ROSMessages::std_msgs::String > ROSMessage
AOverlapSensor(const FObjectInitializer &ObjectInitializer)
FString VehicleName
void CreateLogFile() override
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
virtual void BeginPlay() override
UBoundsVisualizerComponent * BoundsComponent
Definition: OverlapSensor.h:98
void Init(FOverlapSensorParameters InParameters)
void BuildAndSendMessage(const FString &Prefix, AActor *Actor, UPrimitiveComponent *Comp, int32 Index)
void SetVisualizeOverlapArea(bool Visualize)
void SetOverlapBounds(const FVector &NewSize)
FOverlapSensorParameters Parameters
void SetOverlapResponseToAllChannels(const ECollisionResponse Response)
AVehicle * Vehicle
FString TryGetID(AActor *Actor)
FORCEINLINE bool IsValidOverlap(const AActor *OtherActor, const UPrimitiveComponent *OtherComp) const
void OnOverlapBegin(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
void SetSimulateSensor(bool SimulateSensor)
Definition: Sensor.h:160
UTopic * GetROSTopic() const
Definition: Sensor.h:150
AVehicle * IsAttachedToVehicle() const
Definition: Sensor.cpp:159
ULogFile * LogFile
Definition: Sensor.h:367
virtual FString GetActorID_Implementation() const override
Definition: Sensor.h:216
FString CreateTimeStampString() const
Definition: Sensor.cpp:330
FString FileSavePath
Definition: Sensor.h:372
void WriteToLogFile(const FString &Message)
Definition: Sensor.cpp:302
FORCEINLINE bool IsROSConnected() const
Definition: Sensor.h:201
virtual void CreateROSTopic()
Definition: Sensor.cpp:197
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
static FString GetDataFolder()
void Create(const FString &FileNameWithoutExtension, FLogFileSettings Settings)
Definition: LogFile.cpp:40
bool KeepFileOpen
Definition: LogFile.h:42
bool Timestamp
Definition: LogFile.h:39
FString FilePath
Definition: LogFile.h:54
FFileWriteOptions FileWriteOptions
Definition: LogFile.h:45
int32 QueueLength
Definition: LogFile.h:48
bool OverrideFilePath
Definition: LogFile.h:51
FFileCreationOptions FileCreationOptions
Definition: LogFile.h:36