1 package de.dlr.shepard.context.references.timeseriesreference.services;
2
3 import de.dlr.shepard.auth.permission.services.PermissionsService;
4 import de.dlr.shepard.auth.users.entities.User;
5 import de.dlr.shepard.auth.users.services.UserService;
6 import de.dlr.shepard.common.exceptions.InvalidAuthException;
7 import de.dlr.shepard.common.exceptions.InvalidPathException;
8 import de.dlr.shepard.common.exceptions.InvalidRequestException;
9 import de.dlr.shepard.common.util.DateHelper;
10 import de.dlr.shepard.context.collection.entities.DataObject;
11 import de.dlr.shepard.context.collection.services.CollectionService;
12 import de.dlr.shepard.context.collection.services.DataObjectService;
13 import de.dlr.shepard.context.references.IReferenceService;
14 import de.dlr.shepard.context.references.timeseriesreference.daos.ReferencedTimeseriesNodeEntityDAO;
15 import de.dlr.shepard.context.references.timeseriesreference.daos.TimeseriesReferenceDAO;
16 import de.dlr.shepard.context.references.timeseriesreference.io.TimeseriesReferenceIO;
17 import de.dlr.shepard.context.references.timeseriesreference.model.ReferencedTimeseriesNodeEntity;
18 import de.dlr.shepard.context.references.timeseriesreference.model.TimeseriesReference;
19 import de.dlr.shepard.context.version.services.VersionService;
20 import de.dlr.shepard.data.timeseries.io.TimeseriesWithDataPoints;
21 import de.dlr.shepard.data.timeseries.model.Timeseries;
22 import de.dlr.shepard.data.timeseries.model.TimeseriesContainer;
23 import de.dlr.shepard.data.timeseries.model.TimeseriesDataPointsQueryParams;
24 import de.dlr.shepard.data.timeseries.model.enums.AggregateFunction;
25 import de.dlr.shepard.data.timeseries.model.enums.CsvFormat;
26 import de.dlr.shepard.data.timeseries.model.enums.FillOption;
27 import de.dlr.shepard.data.timeseries.services.TimeseriesContainerService;
28 import de.dlr.shepard.data.timeseries.services.TimeseriesCsvService;
29 import de.dlr.shepard.data.timeseries.services.TimeseriesService;
30 import de.dlr.shepard.data.timeseries.utilities.TimeseriesValidator;
31 import io.quarkus.logging.Log;
32 import jakarta.enterprise.context.RequestScoped;
33 import jakarta.inject.Inject;
34 import jakarta.ws.rs.NotFoundException;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Set;
40 import java.util.UUID;
41
42 @RequestScoped
43 public class TimeseriesReferenceService implements IReferenceService<TimeseriesReference, TimeseriesReferenceIO> {
44
45 @Inject
46 TimeseriesReferenceDAO timeseriesReferenceDAO;
47
48 @Inject
49 TimeseriesService timeseriesService;
50
51 @Inject
52 TimeseriesCsvService timeseriesCsvService;
53
54 @Inject
55 DataObjectService dataObjectService;
56
57 @Inject
58 ReferencedTimeseriesNodeEntityDAO timeseriesDAO;
59
60 @Inject
61 UserService userService;
62
63 @Inject
64 CollectionService collectionService;
65
66 @Inject
67 TimeseriesContainerService timeseriesContainerService;
68
69 @Inject
70 VersionService versionService;
71
72 @Inject
73 DateHelper dateHelper;
74
75 @Inject
76 PermissionsService permissionsService;
77
78
79
80
81
82
83
84
85
86
87
88 @Override
89 public List<TimeseriesReference> getAllReferencesByDataObjectId(
90 long collectionShepardId,
91 long dataObjectShepardId,
92 UUID versionUID
93 ) {
94 dataObjectService.getDataObject(collectionShepardId, dataObjectShepardId, versionUID);
95
96 var references = timeseriesReferenceDAO.findByDataObjectShepardId(dataObjectShepardId);
97 return references;
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111 @Override
112 public TimeseriesReference getReference(
113 long collectionShepardId,
114 long dataObjectShepardId,
115 long shepardId,
116 UUID versionUID
117 ) {
118 dataObjectService.getDataObject(collectionShepardId, dataObjectShepardId, versionUID);
119
120 TimeseriesReference reference = timeseriesReferenceDAO.findByShepardId(shepardId, versionUID);
121 if (reference == null || reference.isDeleted()) {
122 String errorMsg = "ID ERROR - Timeseries Reference with id %s is null or deleted".formatted(shepardId);
123 Log.error(errorMsg);
124 throw new InvalidPathException(errorMsg);
125 }
126
127 if (reference.getDataObject() == null || !reference.getDataObject().getShepardId().equals(dataObjectShepardId)) {
128 String errorMsg = "ID ERROR - There is no association between dataObject and reference";
129 Log.error(errorMsg);
130 throw new InvalidPathException(errorMsg);
131 }
132
133 return reference;
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147 @Override
148 public TimeseriesReference createReference(
149 long collectionShepardId,
150 long dataObjectShepardId,
151 TimeseriesReferenceIO timeseriesReference
152 ) {
153 DataObject dataObject = dataObjectService.getDataObject(collectionShepardId, dataObjectShepardId);
154 collectionService.assertIsAllowedToEditCollection(collectionShepardId);
155
156 User user = userService.getCurrentUser();
157
158 TimeseriesContainer container;
159 try {
160 container = timeseriesContainerService.getContainer(timeseriesReference.getTimeseriesContainerId());
161 } catch (InvalidPathException ex) {
162 Log.error(ex.getMessage());
163 throw new InvalidRequestException(ex.getMessage());
164 }
165
166
167 timeseriesReference
168 .getTimeseries()
169 .forEach(timeseries -> TimeseriesValidator.assertTimeseriesPropertiesAreValid(timeseries));
170
171 var toCreate = new TimeseriesReference();
172 toCreate.setCreatedAt(dateHelper.getDate());
173 toCreate.setCreatedBy(user);
174 toCreate.setDataObject(dataObject);
175 toCreate.setName(timeseriesReference.getName());
176 toCreate.setStart(timeseriesReference.getStart());
177 toCreate.setEnd(timeseriesReference.getEnd());
178 toCreate.setTimeseriesContainer(container);
179
180 for (var ts : timeseriesReference.getTimeseries()) {
181 var found = timeseriesDAO.find(
182 ts.getMeasurement(),
183 ts.getDevice(),
184 ts.getLocation(),
185 ts.getSymbolicName(),
186 ts.getField()
187 );
188 if (found != null) {
189 toCreate.addTimeseries(found);
190 } else {
191 toCreate.addTimeseries(new ReferencedTimeseriesNodeEntity(ts));
192 }
193 }
194 TimeseriesReference created = timeseriesReferenceDAO.createOrUpdate(toCreate);
195 created.setShepardId(created.getId());
196 created = timeseriesReferenceDAO.createOrUpdate(created);
197 versionService.attachToVersionOfVersionableEntityAndReturnVersion(dataObject.getId(), created.getId());
198 return created;
199 }
200
201
202
203
204
205
206
207
208
209
210 @Override
211 public void deleteReference(long collectionShepardId, long dataObjectShepardId, long timeseriesReferenceShepardId) {
212 TimeseriesReference timeseriesReference = getReference(
213 collectionShepardId,
214 dataObjectShepardId,
215 timeseriesReferenceShepardId,
216 null
217 );
218 collectionService.assertIsAllowedToEditCollection(collectionShepardId);
219
220 User user = userService.getCurrentUser();
221 timeseriesReference.setDeleted(true);
222 timeseriesReference.setUpdatedAt(dateHelper.getDate());
223 timeseriesReference.setUpdatedBy(user);
224 timeseriesReferenceDAO.createOrUpdate(timeseriesReference);
225 }
226
227 public List<TimeseriesWithDataPoints> getReferencedTimeseriesWithDataPointsList(
228 long collectionShepardId,
229 long dataObjectShepardId,
230 long timeseriesShepardId,
231 AggregateFunction function,
232 Long timeSliceNanoseconds,
233 FillOption fillOption,
234 Set<String> devicesFilterSet,
235 Set<String> locationsFilterSet,
236 Set<String> symbolicNameFilterSet,
237 Set<String> measurementFilterSet,
238 Set<String> fieldFilterSet
239 ) {
240 TimeseriesReference reference = getReference(collectionShepardId, dataObjectShepardId, timeseriesShepardId, null);
241
242 if (reference.getTimeseriesContainer() == null || reference.getTimeseriesContainer().isDeleted()) {
243 String errorMsg =
244 "Referenced Timeseries Container from reference with id %s is null or has been deleted".formatted(
245 timeseriesShepardId
246 );
247 Log.error(errorMsg);
248 throw new NotFoundException(errorMsg);
249 }
250
251 try {
252
253 timeseriesContainerService.getContainer(reference.getTimeseriesContainer().getId());
254 } catch (InvalidPathException ex) {
255 throw new NotFoundException(ex.getMessage());
256 }
257
258 var timeseriesList = reference.getReferencedTimeseriesList().stream().map(ts -> ts.toTimeseries()).toList();
259 var filteredTimeseriesList = timeseriesList
260 .stream()
261 .filter(timeseries ->
262 matchFilter(
263 timeseries,
264 devicesFilterSet,
265 locationsFilterSet,
266 symbolicNameFilterSet,
267 measurementFilterSet,
268 fieldFilterSet
269 )
270 )
271 .toList();
272 var containerId = reference.getTimeseriesContainer().getId();
273 TimeseriesDataPointsQueryParams queryParams = new TimeseriesDataPointsQueryParams(
274 reference.getStart(),
275 reference.getEnd(),
276 timeSliceNanoseconds,
277 fillOption,
278 function
279 );
280
281 return timeseriesService.getManyTimeseriesWithDataPoints(containerId, filteredTimeseriesList, queryParams);
282 }
283
284 public InputStream exportReferencedTimeseriesByShepardId(
285 long collectionShepardId,
286 long dataObjectShepardId,
287 long timeseriesShepardId,
288 AggregateFunction function,
289 Long timeSliceNanoseconds,
290 FillOption fillOption,
291 Set<String> devicesFilterSet,
292 Set<String> locationsFilterSet,
293 Set<String> symbolicNameFilterSet,
294 Set<String> measurementFilterSet,
295 Set<String> fieldFilterSet,
296 CsvFormat csvFormat
297 ) throws IOException {
298 TimeseriesReference reference = getReference(collectionShepardId, dataObjectShepardId, timeseriesShepardId, null);
299
300 if (reference.getTimeseriesContainer() == null || reference.getTimeseriesContainer().isDeleted()) {
301 String errorMsg =
302 "The referenced TimeseriesContainer is null or deleted for Reference with id %s".formatted(timeseriesShepardId);
303 Log.error(errorMsg);
304 throw new NotFoundException(errorMsg);
305 }
306
307 try {
308 timeseriesContainerService.getContainer(reference.getTimeseriesContainer().getId());
309 } catch (InvalidPathException ex) {
310 throw new InvalidRequestException(ex.getMessage());
311 }
312
313 var timeseriesList = reference.getReferencedTimeseriesList().stream().map(ts -> ts.toTimeseries()).toList();
314 var filteredTimeseriesList = timeseriesList
315 .stream()
316 .filter(timeseries ->
317 matchFilter(
318 timeseries,
319 devicesFilterSet,
320 locationsFilterSet,
321 symbolicNameFilterSet,
322 measurementFilterSet,
323 fieldFilterSet
324 )
325 )
326 .toList();
327 var containerId = reference.getTimeseriesContainer().getId();
328 TimeseriesDataPointsQueryParams queryParams = new TimeseriesDataPointsQueryParams(
329 reference.getStart(),
330 reference.getEnd(),
331 timeSliceNanoseconds,
332 fillOption,
333 function
334 );
335
336 return timeseriesCsvService.exportManyTimeseriesWithDataPointsToCsv(
337 containerId,
338 filteredTimeseriesList,
339 queryParams,
340 csvFormat
341 );
342 }
343
344 public InputStream exportReferencedTimeseriesByShepardId(
345 long collectionShepardId,
346 long dataObjectShepardId,
347 long referenceId,
348 CsvFormat csvFormat
349 ) throws IOException {
350 return exportReferencedTimeseriesByShepardId(
351 collectionShepardId,
352 dataObjectShepardId,
353 referenceId,
354 null,
355 null,
356 null,
357 Collections.emptySet(),
358 Collections.emptySet(),
359 Collections.emptySet(),
360 Collections.emptySet(),
361 Collections.emptySet(),
362 csvFormat
363 );
364 }
365
366 private boolean matchFilter(
367 Timeseries timeseries,
368 Set<String> device,
369 Set<String> location,
370 Set<String> symName,
371 Set<String> measurement,
372 Set<String> field
373 ) {
374 var deviceMatches = true;
375 var locationMatches = true;
376 var symbolicNameMatches = true;
377 var measurementMatches = true;
378 var fieldMatches = true;
379 if (!device.isEmpty()) {
380 deviceMatches = device.contains(timeseries.getDevice());
381 }
382 if (!location.isEmpty()) {
383 locationMatches = location.contains(timeseries.getLocation());
384 }
385 if (!symName.isEmpty()) {
386 symbolicNameMatches = symName.contains(timeseries.getSymbolicName());
387 }
388 if (!measurement.isEmpty()) {
389 measurementMatches = measurement.contains(timeseries.getMeasurement());
390 }
391 if (!field.isEmpty()) {
392 fieldMatches = field.contains(timeseries.getField());
393 }
394 return deviceMatches && locationMatches && symbolicNameMatches && measurementMatches && fieldMatches;
395 }
396 }