Agrarsense
Sensor.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 "Sensor.h"
7#include "SensorManager.h"
8#include "SemanticColors.h"
9#include "SensorFactory.h"
10
18
19#include "Misc/DateTime.h"
20#include "Kismet/GameplayStatics.h"
21#include "TimerManager.h"
22#include "UObject/Class.h"
23
24TArray<TWeakObjectPtr<UPrimitiveComponent>> ASensor::ComponentsToHide;
25FPrimitiveAdded ASensor::OnPrimitiveAdded;
26
27ASensor::ASensor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
28{
29 RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
30 RootComponent->SetMobility(EComponentMobility::Movable);
31}
32
34{
35 Super::BeginPlay();
36
37 UWorld* World = GetWorld();
38
40 if (ROSHandler)
41 {
42 // Subscribe to ROS changed event
43 ROSHandler->OnROSStateChanged.AddUniqueDynamic(this, &ASensor::ROSBridgeStateChanged);
44 }
45
47 if (ROSInstance)
48 {
49 // Check if ROS is connected at BeginPlay
50 ROSConnected = ROSInstance->IsROSConnected();
51 }
52
53 // Delay logging sensor information message so each sensor have time setup fully.
54 FTimerHandle Handle;
55 World->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([this]
56 {
57 if (IsValid(this))
58 {
59 FString ActorInformation = IActorInformation::Execute_GetActorInformation(this);
60 FString Message = FString::Printf(TEXT("Spawned %s"), *ActorInformation);
61
62 SimulatorLog::Log(Message);
63 }
64 }), 0.150f, false);
65
66 // Inform sensor manager that new sensor has been spawned.
68
69 ESensorTypes ThisSensorType = GetSensorType();
70
71 if (ThisSensorType != ESensorTypes::NONE &&
72 ThisSensorType != ESensorTypes::Transform &&
73 ThisSensorType != ESensorTypes::Collision &&
74 ThisSensorType != ESensorTypes::Overlap)
75 {
76 // Spawn a Transform sensor for any sensor type other than NONE, Transform, Collision, or Overlap.
77 // Meaning each sensor, like a Lidar sensor, has its own Transform sensor that publishes its:
78 // - World location
79 // - GNSS location (if world has GNSS setup)
80
81 // We don't need to keep track of this TransformSensor Actor as it should
82 // be automatically destroyed when the parent sensor is destroyed (ASensor EndPlay)
83 const FString ActorID = IActorInformation::Execute_GetActorID(this);
84 const FString SensorTransformSensorID = ActorID + "/transform";
85
86 FSensorSpawnParameters SpawnParams;
87 SpawnParams.Transform = GetActorTransform();
88 SpawnParams.SensorIdentifier = SensorTransformSensorID;
89 SpawnParams.SensorName = "transform";
90 SpawnParams.SimulateSensor = true;
91 SpawnParams.Parent = this;
92
93 FTransformSensorParameters SensorParams;
94 SensorParams.SaveTransformDataToDisk = false;
95 SensorParams.OwningActor = this;
96
97 USensorFactory::SpawnTransformSensor(SpawnParams, SensorParams);
98 }
99}
100
101void ASensor::EndPlay(const EEndPlayReason::Type EndPlayReason)
102{
103 // Broadcast that this sensor will be destroyed
104 OnSensorDestroy.Broadcast(this);
105
107
108 Super::EndPlay(EndPlayReason);
109
110 UROSHandler* ROSHandler = UAgrarsenseStatics::GetROSHandle(GetWorld());
111 if (ROSHandler)
112 {
113 ROSHandler->OnROSStateChanged.RemoveDynamic(this, &ASensor::ROSBridgeStateChanged);
114 }
115
116 const FString Sensor = UEnum::GetDisplayValueAsText(GetSensorType()).ToString();
117 const FString ID = GetSensorIdentifier();
118 const FString Message = FString::Printf(TEXT("Destroyed %s sensor with ID: %s"), *Sensor, *ID);
119 SimulatorLog::Log(Message);
120
121 if (EndPlayReason == EEndPlayReason::EndPlayInEditor
122 || EndPlayReason == EEndPlayReason::Quit)
123 {
124 for (auto& WeakComponentPtr : ComponentsToHide)
125 {
126 WeakComponentPtr.Reset();
127 }
128 ComponentsToHide.Empty();
129 }
130
131 // Destroy all Actors that are attached to this sensor like:
132 // Sensor model Actor and TransformSensor Actor.
133 TArray<AActor*> AttachedActors;
134 GetAttachedActors(AttachedActors);
135 for (AActor* Actor : AttachedActors)
136 {
137 if (Actor)
138 {
139 Actor->Destroy();
140 }
141 }
142
143 ROSInstance = nullptr;
144
145 if (LogFile)
146 {
147 LogFile->Destroy();
148 LogFile = nullptr;
149 }
150
152}
153
154FString ASensor::ExportToJsonFile(const FString& FileName)
155{
156 return USimulatorJsonExporter::ExportSensorToJSON(FileName, this);
157}
158
160{
161 AVehicle* VehicleActor = Cast<AVehicle>(ParentActor);
162 if (VehicleActor)
163 {
164 return VehicleActor;
165 }
166 return nullptr;
167}
168
170{
171 switch (ROSState)
172 {
174 ROSConnected = true;
176 break;
177
179 ROSConnected = false;
181 break;
182 }
183}
184
186{
187 if (ROSTopic)
188 {
189 ROSTopic->Unadvertise();
190 ROSTopic->Unsubscribe();
191 ROSTopic->MarkAsDisconnected();
192 ROSTopic->ConditionalBeginDestroy();
193 ROSTopic = nullptr;
194 }
195}
196
198{
199 if (!SendDataToROS
200 || ROSTopic
201 || !ROSInstance
202 || !IsROSConnected())
203 {
204 return;
205 }
206
207 FString MessageType;
208 ESensorTypes SensorType = GetSensorType();
209
210 switch (SensorType)
211 {
214 MessageType = "sensor_msgs/PointCloud2";
215 break;
216
222 case ESensorTypes::DVSCamera: // DVS Camera should override this.
223 MessageType = "sensor_msgs/Image";
224 break;
225
227 MessageType = "geometry_msgs/Transform";
228 break;
229
232 MessageType = "std_msgs/String";
233 break;
234
235 default:
236 FString SensorNameString = UEnumUtilities::ConvertSensorTypeToString(SensorType);
237 UE_LOG(LogTemp, Warning, TEXT("Unhandled ROS sensor message creation for sensor: %s"), *SensorNameString);
238 break;
239 }
240
241 const FString TopicName = FString::Printf(TEXT("/agrarsense/out/sensors/%s"), *GetSensorIdentifier());
242
243 ROSTopic = NewObject<UTopic>(UTopic::StaticClass());
244 if (ROSTopic)
245 {
246 ROSTopic->Init(ROSInstance->ROSIntegrationCore, TopicName, MessageType);
247 ROSTopic->Advertise();
248 }
249
250 const FString Message = FString::Printf(TEXT("%s sensor created ROS topic. Name: '%s' MessageType: '%s' "), *GetSensorName(), *TopicName, *MessageType);
251 SimulatorLog::Log(Message);
252}
253
255{
256 if (FileSavePath.IsEmpty())
257 {
258 const FString DataLocation = UAgrarsensePaths::GetDataFolder();
259
261 {
262 FString VehicleName = Vehicle->GetActorID_Implementation();
263 FileSavePath = DataLocation + VehicleName + "/" + GetActorID_Implementation() + "/";
264 }
265 else
266 {
267 FileSavePath = DataLocation + GetActorID_Implementation() + "/";
268 }
269 }
270}
271
273{
274 if (IsValid(LogFile))
275 {
276 // File has already been created, return
277 return;
278 }
279
280 FLogFileSettings Settings;
283 Settings.QueueLength = 10;
284 Settings.KeepFileOpen = false;
285
286 LogFile = NewObject<ULogFile>(ULogFile::StaticClass());
287 if (LogFile)
288 {
289 // FileName is SensorName_ID_UnixTimeStamp.txt
290 FString UnixTimeStamp = FString::FromInt(FDateTime::Now().ToUnixTimestamp());
291
292 FString ActorID = IActorInformation::Execute_GetActorID(this);
293
294 // Temp fix, Remove all "/" from ActorID
295 ActorID.ReplaceInline(TEXT("/"), TEXT(""), ESearchCase::IgnoreCase);
296
297 FString FileName = FString::Printf(TEXT("%s_%s_%s"), *GetSensorName(), *ActorID, *UnixTimeStamp);
298 LogFile->Create(FileName, Settings);
299 }
300}
301
302void ASensor::WriteToLogFile(const FString& Message)
303{
304 if (!IsValid(LogFile) && IsValid(this))
305 {
307 }
308
309 if (IsValid(LogFile))
310 {
311 LogFile->Write(Message);
312 }
313}
314
315void ASensor::HideComponentForAllCameras(UPrimitiveComponent* PrimitiveComponent)
316{
317 if (PrimitiveComponent)
318 {
319 TWeakObjectPtr<UPrimitiveComponent> Component(PrimitiveComponent);
320 ComponentsToHide.Add(Component);
321 OnPrimitiveAdded.Broadcast(PrimitiveComponent);
322 }
323}
324
325TMap<FString, FColor> ASensor::GetSemanticColors()
326{
328}
329
331{
332 FDateTime Now = FDateTime::Now();
333 FString TimeStamp = FString::Printf(TEXT("%02d:%02d:%04d:%02d:%02d:%02d:%03d"),
334 Now.GetDay(), Now.GetMonth(), Now.GetYear(),
335 Now.GetHour(), Now.GetMinute(), Now.GetSecond(), Now.GetMillisecond());
336
337 return TimeStamp;
338}
EROSState
Definition: ROSState.h:16
ESensorTypes
Definition: SensorTypes.h:15
@ SemanticSegmentationCamera
@ InstanceSegmentationCamera
static TArray< TWeakObjectPtr< UPrimitiveComponent > > ComponentsToHide
Definition: Sensor.h:409
virtual void DestroyROSTopic()
Definition: Sensor.cpp:185
virtual void CreateLogFile()
Definition: Sensor.cpp:272
void ROSBridgeStateChanged(EROSState ROSState)
Definition: Sensor.cpp:169
UROSIntegrationGameInstance * ROSInstance
Definition: Sensor.h:375
static void HideComponentForAllCameras(UPrimitiveComponent *PrimitiveComponent)
Definition: Sensor.cpp:315
virtual void BeginPlay() override
Definition: Sensor.cpp:33
FSensorDestroy OnSensorDestroy
Definition: Sensor.h:283
static FPrimitiveAdded OnPrimitiveAdded
Definition: Sensor.h:377
FString GetSensorIdentifier() const
Definition: Sensor.h:75
UTopic * ROSTopic
Definition: Sensor.h:361
virtual void CreateDataSavePath()
Definition: Sensor.cpp:254
AActor * ParentActor
Definition: Sensor.h:370
FString GetSensorName() const
Definition: Sensor.h:96
virtual ESensorTypes GetSensorType() const
Definition: Sensor.h:65
AVehicle * IsAttachedToVehicle() const
Definition: Sensor.cpp:159
ULogFile * LogFile
Definition: Sensor.h:367
FString ExportToJsonFile(const FString &FileName)
Definition: Sensor.cpp:154
virtual FString GetActorID_Implementation() const override
Definition: Sensor.h:216
bool SendDataToROS
Definition: Sensor.h:364
FString CreateTimeStampString() const
Definition: Sensor.cpp:330
bool ROSConnected
Definition: Sensor.h:407
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 EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: Sensor.cpp:101
virtual void CreateROSTopic()
Definition: Sensor.cpp:197
ASensor(const FObjectInitializer &ObjectInitializer)
Definition: Sensor.cpp:27
static TMap< FString, FColor > GetSemanticColors()
Definition: Sensor.cpp:325
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
static FString GetDataFolder()
static UROSIntegrationGameInstance * GetROSGameInstance(const UObject *WorldContextObject)
static UROSHandler * GetROSHandle(const UObject *WorldContextObject)
static FString ConvertSensorTypeToString(ESensorTypes Sensortype)
void Destroy()
Definition: LogFile.cpp:174
void Write(const FString &Text)
Definition: LogFile.cpp:123
void Create(const FString &FileNameWithoutExtension, FLogFileSettings Settings)
Definition: LogFile.cpp:40
FROSDelegate_ROState OnROSStateChanged
Definition: ROSHandler.h:81
static TMap< FString, FColor > GetSemanticColors()
static ATransformSensor * SpawnTransformSensor(const FSensorSpawnParameters SpawnParameters, FTransformSensorParameters SensorParameters)
static void AddSensor(ASensor *SensorPtr)
static void RemoveSensor(ASensor *SensorPtr)
static FString ExportSensorToJSON(const FString &FileName, ASensor *Sensor)
bool KeepFileOpen
Definition: LogFile.h:42
FFileWriteOptions FileWriteOptions
Definition: LogFile.h:45
int32 QueueLength
Definition: LogFile.h:48
FFileCreationOptions FileCreationOptions
Definition: LogFile.h:36