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
16
17#include "Kismet/GameplayStatics.h"
18#include "TimerManager.h"
19#include "UObject/Class.h"
20
21TArray<TWeakObjectPtr<UPrimitiveComponent>> ASensor::ComponentsToHide;
22FPrimitiveAdded ASensor::OnPrimitiveAdded;
23
24ASensor::ASensor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
25{
26 RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
27 RootComponent->SetMobility(EComponentMobility::Movable);
28}
29
31{
32 Super::BeginPlay();
33
34 UWorld* World = GetWorld();
35
37 if (ROSHandler)
38 {
39 // Subscribe to ROS changed event
40 ROSHandler->OnROSStateChanged.AddUniqueDynamic(this, &ASensor::ROSBridgeStateChanged);
41 }
42
44 if (ROSInstance)
45 {
46 // Check if ROS is connected at BeginPlay
47 ROSConnected = ROSInstance->IsROSConnected();
48 }
49
50 // Delay logging sensor information message so each sensor have time setup fully.
51 FTimerHandle Handle;
52 World->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([this]
53 {
54 if (IsValid(this))
55 {
56 FString ActorInformation = IActorInformation::Execute_GetActorInformation(this);
57 FString Message = FString::Printf(TEXT("Spawned %s"), *ActorInformation);
58
59 SimulatorLog::Log(Message);
60 }
61 }), 0.150f, false);
62
63 // Inform sensor manager that new sensor has been spawned.
65
66
67 ESensorTypes ThisSensorType = GetSensorType();
68
69 if (ThisSensorType != ESensorTypes::NONE &&
70 ThisSensorType != ESensorTypes::Transform &&
71 ThisSensorType != ESensorTypes::Collision &&
72 ThisSensorType != ESensorTypes::Overlap)
73 {
74 // Spawn a Transform sensor for any sensor type other than NONE, Transform, Collision, or Overlap.
75 // Meaning each sensor, like a Lidar sensor, has its own Transform sensor that publishes its:
76 // - World location
77 // - GNSS location (if world has GNSS setup)
78
79 // We don't need to keep track of this TransformSensor Actor as it should
80 // be automatically destroyed when the parent sensor is destroyed (ASensor EndPlay)
81 FString ActorID = IActorInformation::Execute_GetActorID(this);
82 FString SensorTransformSensorID = ActorID + "/transform";
83 USensorFactory::SpawnTransformSensor(GetActorTransform(), this, SensorTransformSensorID, "transform", true);
84 }
85}
86
87void ASensor::EndPlay(const EEndPlayReason::Type EndPlayReason)
88{
89 // Broadcast that this sensor will be destroyed
90 OnSensorDestroy.Broadcast(this);
91
93
94 Super::EndPlay(EndPlayReason);
95
96 UROSHandler* ROSHandler = UAgrarsenseStatics::GetROSHandle(GetWorld());
97 if (ROSHandler)
98 {
99 ROSHandler->OnROSStateChanged.RemoveDynamic(this, &ASensor::ROSBridgeStateChanged);
100 }
101
102 FString Sensor = UEnum::GetDisplayValueAsText(GetSensorType()).ToString();
103 FString ID = GetSensorIdentifier();
104 FString Message = FString::Printf(TEXT("Destroyed %s sensor with ID: %s"), *Sensor, *ID);
105 SimulatorLog::Log(Message);
106
107 if (EndPlayReason == EEndPlayReason::EndPlayInEditor
108 || EndPlayReason == EEndPlayReason::Quit)
109 {
110 for (auto& WeakComponentPtr : ComponentsToHide)
111 {
112 WeakComponentPtr.Reset();
113 }
114 ComponentsToHide.Empty();
115 }
116
117 // Destroy all Actors that are attached to this sensor like:
118 // Sensor model Actor and TransformSensor Actor.
119 TArray<AActor*> AttachedActors;
120 GetAttachedActors(AttachedActors);
121 for (AActor* Actor : AttachedActors)
122 {
123 if (Actor)
124 {
125 Actor->Destroy();
126 }
127 }
128
129 ROSInstance = nullptr;
130
131 if (LogFile)
132 {
133 LogFile->Destroy();
134 LogFile = nullptr;
135 }
136
138}
139
140FString ASensor::ExportToJsonFile(const FString& FileName)
141{
142 return USimulatorJsonExporter::ExportSensorToJSON(FileName, this);
143}
144
146{
147 switch (ROSState)
148 {
150 ROSConnected = true;
152 break;
153
155 ROSConnected = false;
157 break;
158 }
159}
160
162{
163 if (ROSTopic)
164 {
165 ROSTopic->Unadvertise();
166 ROSTopic->Unsubscribe();
167 ROSTopic->MarkAsDisconnected();
168 ROSTopic->ConditionalBeginDestroy();
169 ROSTopic = nullptr;
170 }
171}
172
174{
175 if (!SendDataToROS
176 || ROSTopic
177 || !ROSInstance
178 || !IsROSConnected())
179 {
180 return;
181 }
182
183 FString MessageType;
184 ESensorTypes SensorType = GetSensorType();
185
186 switch (SensorType)
187 {
190 MessageType = "sensor_msgs/PointCloud2";
191 break;
192
197 case ESensorTypes::DVSCamera: // DVS Camera should override this.
198 MessageType = "sensor_msgs/Image";
199 break;
200
202 MessageType = "geometry_msgs/Transform";
203 break;
204
207 MessageType = "std_msgs/String";
208 break;
209
210 default:
211 FString SensorNameString = UEnumUtilities::ConvertSensorTypeToString(SensorType);
212 UE_LOG(LogTemp, Warning, TEXT("Unhandled ROS sensor message creation for sensor: %s"), *SensorNameString);
213 break;
214 }
215
216 FString TopicName = FString::Printf(TEXT("/agrarsense/out/sensors/%s"), *GetSensorIdentifier());
217
218 ROSTopic = NewObject<UTopic>(UTopic::StaticClass());
219 if (ROSTopic)
220 {
221 ROSTopic->Init(ROSInstance->ROSIntegrationCore, TopicName, MessageType);
222 ROSTopic->Advertise();
223 }
224
225 FString Message = FString::Printf(TEXT("%s sensor created ROS topic. Name: '%s' MessageType: '%s' "), *GetSensorName(), *TopicName, *MessageType);
226 SimulatorLog::Log(Message);
227}
228
230{
231 if (FileSavePath.IsEmpty())
232 {
233 FString DataLocation = UAgrarsensePaths::GetDataFolder();
234 FString UnixTimeStamp = FString::FromInt(FDateTime::Now().ToUnixTimestamp());
235 FileSavePath = DataLocation + GetSensorIdentifier() + "_" + UnixTimeStamp + "/";
236 }
237}
238
240{
241 if (IsValid(LogFile))
242 {
243 // File has already been created, return
244 return;
245 }
246
247 FLogFileSettings Settings;
250 Settings.QueueLength = 10;
251 Settings.KeepFileOpen = false;
252
253 LogFile = NewObject<ULogFile>(ULogFile::StaticClass());
254 if (LogFile)
255 {
256 // FileName is SensorName_ID_UnixTimeStamp.txt
257 FString UnixTimeStamp = FString::FromInt(FDateTime::Now().ToUnixTimestamp());
258
259 FString ActorID = IActorInformation::Execute_GetActorID(this);
260
261 // Temp fix, Remove all "/" from ActorID
262 ActorID.ReplaceInline(TEXT("/"), TEXT(""), ESearchCase::IgnoreCase);
263
264 FString FileName = FString::Printf(TEXT("%s_%s_%s"), *GetSensorName(), *ActorID, *UnixTimeStamp);
265 LogFile->Create(FileName, Settings);
266 }
267}
268
269void ASensor::WriteToLogFile(const FString& Message)
270{
271 if (!IsValid(LogFile) && IsValid(this))
272 {
274 }
275
276 if (IsValid(LogFile))
277 {
278 LogFile->Write(Message);
279 }
280}
281
282void ASensor::HideComponentForAllCameras(UPrimitiveComponent* PrimitiveComponent)
283{
284 if (PrimitiveComponent)
285 {
286 TWeakObjectPtr<UPrimitiveComponent> Component(PrimitiveComponent);
287 ComponentsToHide.Add(Component);
288 OnPrimitiveAdded.Broadcast(PrimitiveComponent);
289 }
290}
291
292TMap<FString, FColor> ASensor::GetSemanticColors()
293{
295}
EROSState
Definition: ROSState.h:16
ESensorTypes
Definition: SensorTypes.h:15
@ SemanticSegmentationCamera
static TArray< TWeakObjectPtr< UPrimitiveComponent > > ComponentsToHide
Definition: Sensor.h:386
virtual void DestroyROSTopic()
Definition: Sensor.cpp:161
virtual void CreateLogFile()
Definition: Sensor.cpp:239
void ROSBridgeStateChanged(EROSState ROSState)
Definition: Sensor.cpp:145
UROSIntegrationGameInstance * ROSInstance
Definition: Sensor.h:352
static void HideComponentForAllCameras(UPrimitiveComponent *PrimitiveComponent)
Definition: Sensor.cpp:282
virtual void BeginPlay() override
Definition: Sensor.cpp:30
FSensorDestroy OnSensorDestroy
Definition: Sensor.h:269
static FPrimitiveAdded OnPrimitiveAdded
Definition: Sensor.h:354
FString GetSensorIdentifier() const
Definition: Sensor.h:74
UTopic * ROSTopic
Definition: Sensor.h:341
virtual void CreateDataSavePath()
Definition: Sensor.cpp:229
FString GetSensorName() const
Definition: Sensor.h:95
virtual ESensorTypes GetSensorType() const
Definition: Sensor.h:64
ULogFile * LogFile
Definition: Sensor.h:347
FString ExportToJsonFile(const FString &FileName)
Definition: Sensor.cpp:140
bool SendDataToROS
Definition: Sensor.h:344
bool ROSConnected
Definition: Sensor.h:384
FString FileSavePath
Definition: Sensor.h:349
void WriteToLogFile(const FString &Message)
Definition: Sensor.cpp:269
FORCEINLINE bool IsROSConnected() const
Definition: Sensor.h:192
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: Sensor.cpp:87
virtual void CreateROSTopic()
Definition: Sensor.cpp:173
ASensor(const FObjectInitializer &ObjectInitializer)
Definition: Sensor.cpp:24
static TMap< FString, FColor > GetSemanticColors()
Definition: Sensor.cpp:292
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:170
void Write(const FString &Text)
Definition: LogFile.cpp:119
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 FTransform &transform, AActor *Owner, const FString sensorIdentifier, const FString sensorName, bool SimulateSensor=true)
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