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