001/** 002 * Copyright 2016 Tampere University of Technology, Pori Department 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package service.tut.pori.apilta.sensors.reference; 017 018import java.time.Instant; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Date; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Random; 025import java.util.Set; 026import java.util.TreeMap; 027import java.util.UUID; 028 029import org.apache.commons.collections4.IterableUtils; 030import org.apache.commons.lang3.ArrayUtils; 031import org.apache.commons.lang3.RandomUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.commons.text.RandomStringGenerator; 034import org.apache.log4j.Logger; 035 036import core.tut.pori.http.parameters.DataGroups; 037import core.tut.pori.http.parameters.DateIntervalParameter.Interval; 038import core.tut.pori.http.parameters.Limits; 039import core.tut.pori.users.UserIdentity; 040import service.tut.pori.apilta.sensors.Definitions; 041import service.tut.pori.apilta.sensors.datatypes.Condition; 042import service.tut.pori.apilta.sensors.datatypes.DataPoint; 043import service.tut.pori.apilta.sensors.datatypes.Measurement; 044import service.tut.pori.apilta.sensors.datatypes.MeasurementList; 045import service.tut.pori.apilta.sensors.datatypes.Output; 046import service.tut.pori.apilta.sensors.datatypes.SensorTask; 047import service.tut.pori.tasks.datatypes.Task; 048import service.tut.pori.tasks.datatypes.Task.State; 049import service.tut.pori.tasks.datatypes.Task.Visibility; 050import service.tut.pori.tasks.datatypes.TaskBackend; 051import service.tut.pori.tasks.datatypes.TaskBackend.Status; 052 053/** 054 * 055 * Class that can be used to created example objects/object lists. 056 * 057 */ 058public class SensorsXMLObjectCreator { 059 private static final Logger LOGGER = Logger.getLogger(SensorsXMLObjectCreator.class); 060 private static final int MAX_CONDITIONS = 5; 061 private static final int MAX_OUTPUTS = 5; 062 private static final int TEXT_LENGTH = 64; 063 private Random _random = null; 064 private RandomStringGenerator _stringGenerator = null; 065 066 /** 067 * 068 * @param seed for random generator, or null to use default (system time in nanoseconds) 069 */ 070 public SensorsXMLObjectCreator(Long seed){ 071 if(seed == null){ 072 seed = System.nanoTime(); 073 } 074 _random = new Random(seed); 075 _stringGenerator = new RandomStringGenerator.Builder().withinRange('a', 'z').build(); 076 } 077 078 /** 079 * @return the random 080 */ 081 public Random getRandom() { 082 return _random; 083 } 084 085 /** 086 * 087 * @param backendIdFilter 088 * @param createdFilter 089 * @param dataGroups 090 * @param limits 091 * @param measurementIdFilter 092 * @return list of measurement objects 093 */ 094 public List<Measurement> generateMeasurementList(long[] backendIdFilter, Set<Interval> createdFilter, DataGroups dataGroups, Limits limits, List<String> measurementIdFilter){ 095 int count = limits.getMaxItems(service.tut.pori.apilta.sensors.datatypes.Definitions.ELEMENT_MEASUREMENT_LIST); 096 if(count < 1){ 097 LOGGER.warn("count < 1"); 098 return null; 099 }else if(count >= Limits.DEFAULT_MAX_ITEMS){ 100 LOGGER.debug("Count was "+Limits.DEFAULT_MAX_ITEMS+", using 1."); 101 count = 1; 102 } 103 boolean hasMeasurementIdFilter = (measurementIdFilter != null && !measurementIdFilter.isEmpty()); 104 if(hasMeasurementIdFilter){ 105 int filterSize = measurementIdFilter.size(); 106 if(hasMeasurementIdFilter && filterSize > count){ 107 LOGGER.debug("Not enough measurement ids in the filter for limit count, restricting count to: "+filterSize); 108 count = filterSize; 109 } 110 } 111 112 List<Measurement> list = new ArrayList<>(); 113 for(int i=0;i<count;++i) { 114 Measurement measurement = generateMeasurementData(backendIdFilter, createdFilter, dataGroups, limits); 115 if(hasMeasurementIdFilter){ // if filter given ... 116 measurement.setMeasurementId(measurementIdFilter.get(i)); // ... modify ids to match the filter 117 } 118 list.add(measurement); 119 } 120 return (list.isEmpty() ? null : list); 121 } 122 123 /** 124 * 125 * @param backendIdFilter 126 * @param createdFilter 127 * @param dataGroups 128 * @param limits 129 * @return pseudo randomly generated measurement data 130 */ 131 public Measurement generateMeasurementData(long[] backendIdFilter, Set<Interval> createdFilter, DataGroups dataGroups, Limits limits) { 132 Measurement data = new Measurement(); 133 data.setBackendId((ArrayUtils.isEmpty(backendIdFilter) ? Math.abs(_random.nextLong()) : backendIdFilter[_random.nextInt(backendIdFilter.length)])); 134 if(DataGroups.hasDataGroup(DataGroups.DATA_GROUP_ALL, dataGroups) || DataGroups.hasDataGroup(Definitions.DATA_GROUP_DATA_POINTS, dataGroups)){ 135 data.setDataPoints(createDataPointList(data.getMeasurementId(), limits, createdFilter)); 136 } 137 data.setMeasurementId(UUID.randomUUID().toString()); 138 139 return data; 140 } 141 142 /** 143 * 144 * @param backendId 145 * @param taskIds 146 * @param taskType 147 * @return pseudo randomly generated task details 148 * @throws IllegalArgumentException on invalid arguments 149 */ 150 public SensorTask generateTaskDetails(Long backendId, Collection<String> taskIds, String taskType) throws IllegalArgumentException { 151 SensorTask task = (SensorTask) setTaskDetails(new SensorTask(), backendId, taskIds, taskType); 152 setSensorTaskDetails(task); 153 154 return task; 155 } 156 157 /** 158 * 159 * @param backendId 160 * @param dataGroups 161 * @param limits 162 * @param taskIds 163 * @param taskType 164 * @return pseudo randomly generated task results 165 * @throws IllegalArgumentException on invalid arguments 166 */ 167 public SensorTask generateTaskResults(Long backendId, DataGroups dataGroups, Limits limits, Collection<String> taskIds, String taskType) throws IllegalArgumentException 168 { 169 SensorTask task = (SensorTask) setTaskDetails(new SensorTask(), _random.nextLong(), taskIds, taskType); 170 //erase a few non-needed members for this case 171 List<TaskBackend> backends = task.getBackends(); 172 backends.clear(); 173 TaskBackend tb = generateTaskBackend(backendId); 174 backends.add(tb); 175 task.setCreated(null); 176 task.setUpdated(null); 177 task.setUserId(null); 178 task.setName(null); 179 task.setDescription(null); 180 task.setState(null); 181 //generate & set task results 182 MeasurementList data = new MeasurementList(); 183 data.setMeasurements(generateMeasurementList((backendId == null ? new long[]{tb.getBackendId()} : new long[]{backendId}), null, dataGroups, limits, null)); 184 task.setMeasurements(data); 185 186 return task; 187 } 188 189 /** 190 * 191 * @param measurementId 192 * @param limits 193 * @param createdFilter 194 * @return list of datapoint objects 195 */ 196 public List<DataPoint> createDataPointList(String measurementId, Limits limits, Set<Interval> createdFilter){ 197 int count = limits.getMaxItems(service.tut.pori.apilta.sensors.datatypes.Definitions.ELEMENT_DATAPOINT_LIST); 198 if(count < 1){ 199 LOGGER.warn("count < 1"); 200 return null; 201 }else if(count >= Limits.DEFAULT_MAX_ITEMS){ 202 LOGGER.debug("Count was "+Limits.DEFAULT_MAX_ITEMS+", using 1."); 203 count = 1; 204 } 205 List<DataPoint> list = new ArrayList<>(); 206 for(int i=0;i<count;++i){ 207 DataPoint kw = createDataPoint(createdFilter, measurementId); 208 if(kw != null){ 209 list.add(kw); 210 } 211 } 212 return (list.isEmpty() ? null : list); 213 } 214 215 /** 216 * Return pseudo randomly generated DataPoint 217 * @param createdFilter 218 * @param measurementId 219 * @return a datapoint 220 */ 221 public DataPoint createDataPoint(Set<Interval> createdFilter, String measurementId){ 222 DataPoint dp = new DataPoint(); 223 Date created = createDate(createdFilter, null); 224 dp.setCreated(created); 225 dp.setDataPointId(UUID.randomUUID().toString()); 226 227 if(measurementId == null){ 228 measurementId = UUID.randomUUID().toString(); 229 } 230 231 dp.setMeasurementId(measurementId); 232 dp.setDescription(_stringGenerator.generate(TEXT_LENGTH)); 233 dp.setKey(_stringGenerator.generate(TEXT_LENGTH)); 234 dp.setValue(_stringGenerator.generate(TEXT_LENGTH)); 235 236 return dp; 237 } 238 239 /** 240 * 241 * @param intervals if null, the returned date will be random, if given an interval will be randomly selected amongst the given intervals and a new date will be generated that is within the interval 242 * @param start override the start time 243 * @return new date created by the given intervals 244 * @throws IllegalArgumentException on invalid interval and/or start time 245 */ 246 private Date createDate(Set<Interval> intervals, Date start) throws IllegalArgumentException{ 247 if(intervals != null && !intervals.isEmpty()){ 248 Interval interval = IterableUtils.get(intervals, _random.nextInt(intervals.size())); 249 if(start == null){ 250 start = interval.getStart(); 251 } 252 Date end = interval.getEnd(); 253 if(end.before(start)){ 254 throw new IllegalArgumentException("Cannot create valid date based on the given start time."); 255 } 256 257 return new Date(RandomUtils.nextLong(start.getTime(), end.getTime())); 258 }else if(start == null){ 259 return new Date(RandomUtils.nextLong(0, System.currentTimeMillis())); 260 }else{ 261 return new Date(RandomUtils.nextLong(start.getTime(), System.currentTimeMillis())); 262 } 263 } 264 265 /** 266 * 267 * @param task 268 * @param backendId 269 * @param taskIds optional task identifiers (if null or empty, one id will be randomly generated) 270 * @param taskType 271 * @return the populated base class task 272 */ 273 public Task setTaskDetails(Task task, long backendId, Collection<String> taskIds, String taskType){ 274 if(task == null){ 275 return null; 276 } 277 278 ArrayList<TaskBackend> backends = new ArrayList<>(1); 279 TaskBackend backend = new TaskBackend(); 280 backend.setBackendId(backendId); 281 backends.add(backend); 282 task.setBackends(backends); 283 284 task.setCreated(new Date(System.currentTimeMillis() - Math.abs(_random.nextLong() % 31536000000L))); 285 if(taskIds == null || taskIds.isEmpty()) { 286 task.addTaskId(UUID.randomUUID().toString()); 287 }else { 288 for(String taskId : taskIds) { 289 task.addTaskId(taskId); 290 } 291 } 292 293 HashSet<String> taskTypes = new HashSet<>(1); 294 if(StringUtils.isEmpty(taskType)){ 295 taskTypes.add(_stringGenerator.generate(TEXT_LENGTH)); 296 }else{ 297 taskTypes.add(taskType); 298 } 299 task.setTaskTypes(taskTypes); 300 task.setUpdated(new Date(task.getCreated().getTime() + Math.abs(_random.nextLong() % 31536000000L))); 301 task.setUserId(new UserIdentity(Math.abs(_random.nextLong()))); 302 task.setName(_stringGenerator.generate(TEXT_LENGTH)); 303 task.setDescription(_stringGenerator.generate(TEXT_LENGTH)); 304 task.setDataVisibility(generateVisibility()); 305 task.setState(generateState()); 306 307 return task; 308 } 309 310 /** 311 * 312 * @return random task visibility 313 */ 314 public Visibility generateVisibility() { 315 Visibility[] visibilities = Visibility.values(); 316 return visibilities[_random.nextInt(visibilities.length)]; 317 } 318 319 /** 320 * 321 * @return random task state 322 */ 323 public State generateState() { 324 State[] values = State.values(); 325 return values[_random.nextInt(values.length)]; 326 } 327 328 /** 329 * 330 * @param backendId if null, random id is generated 331 * @return random task back end 332 */ 333 public TaskBackend generateTaskBackend(Long backendId) { 334 TaskBackend tb = new TaskBackend(); 335 tb.setBackendId((backendId == null ? Math.abs(_random.nextLong()) : backendId)); 336 tb.setMessage(_stringGenerator.generate(TEXT_LENGTH)); 337 Status[] values = Status.values(); 338 tb.setStatus(values[_random.nextInt(values.length)]); 339 return tb; 340 } 341 342 /** 343 * @param task 344 * @return return populated sensor task 345 */ 346 public Task setSensorTaskDetails(SensorTask task){ 347 List<Output> outputs = new ArrayList<>(); 348 for(int i=-1, count=_random.nextInt(MAX_OUTPUTS); i<count; ++i){ 349 Output output = new Output(); 350 output.setFeature(_stringGenerator.generate(5)); 351 outputs.add(output); 352 } 353 task.setOutput(outputs); 354 355 task.setConditions(createConditionList()); 356 return task; 357 } 358 359 /** 360 * 361 * @return list of generated random conditions 362 */ 363 public List<Condition> createConditionList(){ 364 List<Condition> list = new ArrayList<>(); 365 for(int i=-1, count=_random.nextInt(MAX_CONDITIONS);i<count;++i){ 366 Condition condition = createCondition(); 367 if(condition != null){ 368 list.add(condition); 369 } 370 } 371 return (list.isEmpty() ? null : list); 372 } 373 374 /** 375 * 376 * @return return randomized condition 377 */ 378 public Condition createCondition(){ 379 Condition condition = new Condition(); 380 TreeMap<String, String> conditions = new TreeMap<>(); 381 for(int i=-1, count=_random.nextInt(MAX_CONDITIONS); i<count; ++i){ 382 conditions.put(_stringGenerator.generate(5), _stringGenerator.generate(10)); 383 } 384 conditions.put("time/validFromToRange", Instant.now().minusSeconds(Math.abs(_random.nextLong() % 31536000L)).toString() +"/"+Instant.now().plusSeconds(Math.abs(_random.nextLong() % 31536000L)).toString()); 385 condition.setConditions(conditions); 386 return condition; 387 } 388 389 /** 390 * 391 * @return random GUID 392 */ 393 public String createGUID() { 394 return UUID.randomUUID().toString(); 395 } 396}