001    package edu.nrao.sss.model.project;
002    
003    import java.math.BigDecimal;
004    import java.util.*;
005    
006    import edu.nrao.sss.astronomy.CelestialCoordinateSystem;
007    import edu.nrao.sss.measure.*;
008    import edu.nrao.sss.model.project.scan.*;
009    import edu.nrao.sss.model.resource.*;
010    import edu.nrao.sss.model.resource.evla.EvlaPointingPosition;
011    import edu.nrao.sss.util.EnumerationUtility;
012    import edu.nrao.sss.util.IllegalTransitionException;
013    
014    import edu.nrao.sss.model.project.scheduling.priority.Priority;
015    import edu.nrao.sss.model.project.scheduling.priority.PriorityType;
016    
017    /**
018     * Helper for test classes; builds projects and their components.
019     * <p>
020     * <b>Version Info:</b>
021     * <table style="margin-left:2em">
022     *   <tr><td>$Revision: 2190 $</td></tr>
023     *   <tr><td>$Date: 2009-04-13 15:15:03 -0600 (Mon, 13 Apr 2009) $</td></tr>
024     *   <tr><td>$Author: dharland $</td></tr>
025     * </table></p>
026     *  
027     * @since 2006-10-09
028     */
029    public class ProjectBuilder
030    {
031      private static final EnumerationUtility ENUM_UTIL =
032        EnumerationUtility.getSharedInstance();
033    
034      private Random random = new Random(System.currentTimeMillis());
035    
036      private static int PROJECT_COUNTER  = 0;
037    
038      private boolean setId            = false;
039      private Long    nextProjectId    = 1L;
040      private Long    nextProgBlockId  = 1L;
041      private Long    nextSchedBlockId = 1L;
042    
043      private ScanBuilder scanBuilder = new ScanBuilder();
044      
045      //Used for coordinating multiple intervals
046      private Date timeIntervalEnd = null;
047    
048      //============================================================================
049      // PUBLIC METHODS
050      //============================================================================
051      
052      /**
053       * Tells the factory methods whether or not they should set the object
054       * identifiers.
055       */
056      public void setIdentifiers(boolean setIds)
057      {
058        setId = setIds;
059      }
060    
061      public Project makeProject(TelescopeType telescope)
062      {
063        Project project = (telescope == null) ? makeProject() 
064                                              : Project.createProject(telescope);
065    
066        buildProject(project);
067        
068        return project;
069      }
070    
071      private Project makeProject()
072      {
073        return Project.createProject(random.nextDouble() < 0.2 ? TelescopeType.GBT
074                                                               : TelescopeType.VLA);
075      }
076      
077      public void buildProject(Project project)
078      {
079        if (project == null)
080          project = makeProject();
081        
082        if (setId)
083          project.setId(nextProjectId++);
084        
085        Date now = new Date();
086        project.setCreatedOn(now);
087        project.setLastUpdatedOn(now);
088    
089        long id = 1L + (long)(random.nextInt(1000));
090        project.setCreatedBy(id);
091        project.setLastUpdatedBy(id);
092        project.setEditor(id);
093    
094        project.setTitle("SSS Random Project #" + (++PROJECT_COUNTER));
095        project.setProjectCode(makeCode());
096        project.setProposalCode(makeCode());
097        project.setProjectType(ENUM_UTIL.getRandomValueFor(ProjectType.class));
098        project.setScientificPriority(
099          ENUM_UTIL.getRandomValueFor(ScientificPriority.class));
100        project.setAllocatedTime(10.0 + (double)(random.nextInt(50)));
101        setComment(project);
102        
103        //Program Blocks
104        int pbCount = 1;
105        if (project.getTelescope().equals(TelescopeType.VLA))
106          pbCount = 1 + random.nextInt(4);
107        
108        for (int i=0; i < pbCount; i++)
109          makeProgramBlockFor(project);
110    
111        //Make all PBs prerequisites of the last PB
112        if (pbCount > 1)
113        {
114          List<ProgramBlock> progBlocks = project.getProgramBlocks();
115          ProgramBlock lastPb = progBlocks.get(pbCount - 1);
116          for (int pb=0; pb < (pbCount-1); pb++)
117            lastPb.addPrerequisite(progBlocks.get(pb));
118        }
119      }
120    
121      //----------------------------------------------------------------------------
122      // Program Block
123      //----------------------------------------------------------------------------
124    
125      public ProgramBlock makeProgramBlockFor(Project proj)
126      {
127        //Quick exit if PB is null
128        if (proj == null)
129          return makeProgramBlock();
130        
131        ProgramBlock pb = proj.createProgramBlock();
132        
133        proj.addProgramBlock(pb);
134    
135        fillProgramBlock(pb, true);
136        
137        return pb;
138      }
139    
140      public ProgramBlock makeProgramBlockButNotSchedulingBlocksFor(Project proj)
141      {
142        //Quick exit if PB is null
143        if (proj == null)
144          return makeProgramBlock();
145        
146        ProgramBlock pb = proj.createProgramBlock();
147        
148        proj.addProgramBlock(pb);
149    
150        fillProgramBlock(pb, false);
151        
152        return pb;
153      }
154    
155      public ProgramBlock makeProgramBlock()
156      {
157        ProgramBlock pb = new ProgramBlock();
158    
159        fillProgramBlock(pb, true);
160        
161        return pb;
162      }
163    
164      public ProgramBlock makeProgramBlockButNotSchedulingBlocks()
165      {
166        ProgramBlock pb = new ProgramBlock();
167    
168        fillProgramBlock(pb, false);
169        
170        return pb;
171      }
172      
173      public void fillProgramBlock(ProgramBlock pb, boolean makeSbs)
174      {
175        if (setId)
176          pb.setId(nextProgBlockId++);
177        
178        Date now = new Date();
179        pb.setCreatedOn(now);
180        pb.setLastUpdatedOn(now);
181    
182        long id = 1L + (long)(random.nextInt(1000));
183        pb.setCreatedBy(id);
184        pb.setLastUpdatedBy(id);
185        
186        pb.setName(makeCode());
187        setComment(pb);
188    
189        //Acceptable Telescope Configurations
190        List<TelescopeConfiguration> configurations = pb.getAcceptableConfigurations();
191        int count = 1 + random.nextInt(4);
192        for (int tc=0; tc < count; tc++)
193          configurations.add(ENUM_UTIL.getRandomValueFor(TelescopeConfiguration.class));
194    
195        //Scheduling Blocks
196        count = makeSbs ? 1 + random.nextInt(4) : 0;
197        for (int i=0; i < count; i++)
198          makeSchedulingBlockFor(pb);
199    
200        //Make all SBs prerequisites of the last SB
201        if (count > 1)
202        {
203          List<SchedulingBlock> schedBlocks = pb.getSchedulingBlocks();
204          SchedulingBlock lastSb = schedBlocks.get(count - 1);
205          for (int s=0; s < (count-1); s++)
206            lastSb.addPrerequisite(schedBlocks.get(s));
207        }
208      }
209    
210      //----------------------------------------------------------------------------
211      // Scheduling Block
212      //----------------------------------------------------------------------------
213    
214      public SchedulingBlock makeSchedulingBlockFor(ProgramBlock progBlock)
215      {
216        //Quick exit if PB is null
217        if (progBlock == null)
218          return makeSchedulingBlock();
219        
220        SchedulingBlock sb = progBlock.createSchedulingBlock();
221        
222        progBlock.addSchedulingBlock(sb);
223    
224        fillSchedulingBlock(sb, true);
225        
226        return sb;
227      }
228    
229      public SchedulingBlock makeSchedulingBlockButNotScansFor(ProgramBlock progBlock)
230      {
231        //Quick exit if PB is null
232        if (progBlock == null)
233          return makeSchedulingBlock();
234        
235        SchedulingBlock sb = progBlock.createSchedulingBlock();
236        
237        progBlock.addSchedulingBlock(sb);
238    
239        fillSchedulingBlock(sb, false);
240        
241        return sb;
242      }
243    
244      public SchedulingBlock makeSchedulingBlock()
245      {
246        SchedulingBlock sb = new SchedulingBlock();
247    
248        fillSchedulingBlock(sb, true);
249        
250        return sb;
251      }
252    
253      public SchedulingBlock makeSchedulingBlockButNotScans()
254      {
255        SchedulingBlock sb = new SchedulingBlock();
256    
257        fillSchedulingBlock(sb, false);
258        
259        return sb;
260      }
261      
262      public void fillSchedulingBlock(SchedulingBlock sb, boolean makeScans)
263      {
264        if (setId)
265          sb.setId(nextSchedBlockId++);
266        
267        Date now = new Date();
268        sb.setCreatedOn(now);
269        sb.setLastUpdatedOn(now);
270    
271        long id = 1L + (long)(random.nextInt(1000));
272        sb.setCreatedBy(id);
273        sb.setLastUpdatedBy(id);
274    
275        sb.setName(makeCode());
276        setComment(sb);
277    
278        sb.setAuthorizedCount(1 + random.nextInt(5));
279        sb.setType(ENUM_UTIL.getRandomValueFor(SchedulingType.class));
280        if (sb.getType().equals(SchedulingType.FIXED_DATE))
281          sb.setFixedStartTime(new Date());
282        
283        sb.setLstStartRange(makeTimeOfDayInterval());
284        
285        //Priorities
286        List<Priority> priorities = new ArrayList<Priority>();
287        PriorityType[] types = PriorityType.values();
288        final int priorityNormalizer = 8;
289        for ( PriorityType pt : types ){
290            Priority priority = new Priority();
291            priority.setPriorityType( pt );
292            priority.setPriorityValue( random.nextFloat() * priorityNormalizer );
293            priorities.add( priority );
294        }
295        sb.setPriorities( priorities );
296        
297        //PreCalibrations
298        List<ServiceCalibration> calibs = sb.getServiceCalibrations();
299        int count = random.nextInt(4);
300        for (int sc=0; sc < count; sc++)
301        {
302          ServiceCalibration calib = new ServiceCalibration();
303          calib.setType(ENUM_UTIL.getRandomValueFor(ScanIntent.class));
304          calib.setTiming(ENUM_UTIL.getRandomValueFor(ServiceCalibrationTiming.class));
305          calib.setAllowableSeparation(new TimeDuration(new BigDecimal(20 + random.nextInt(80))));
306          calibs.add(calib);
307        }
308        
309        //Assumed telescope position
310        if (random.nextBoolean())
311        {
312          EvlaPointingPosition antPos = new EvlaPointingPosition();
313          
314          antPos.setCoordinateSystem(ENUM_UTIL.getRandomValueFor(CelestialCoordinateSystem.class));
315          
316          int minAngle = antPos.getLatitudeMinimum().toUnits(ArcUnits.DEGREE).intValue();
317          int maxAngle = antPos.getLatitudeMaximum().toUnits(ArcUnits.DEGREE).intValue();
318          
319          int angle = random.nextInt(maxAngle - minAngle) + minAngle;
320          
321          antPos.setLatitude(new Angle(""+angle));
322          
323          minAngle = antPos.getLongitudeMinimum().toUnits(ArcUnits.DEGREE).intValue();
324          maxAngle = antPos.getLongitudeMaximum().toUnits(ArcUnits.DEGREE).intValue();
325          
326          angle = random.nextInt(maxAngle - minAngle) + minAngle;
327          
328          antPos.setLongitude(new Angle(""+angle));
329          
330          sb.setAssumedTelescopePointing(antPos);
331        }
332        
333        //TODO Environmental Constraints
334        
335        sb.setPreferredDateRange(makeTimeInterval(null));
336    
337        //Scans
338        if (makeScans)
339          makeScansFor(sb);
340      }
341    
342      //----------------------------------------------------------------------------
343      // Execution Block
344      //----------------------------------------------------------------------------
345    
346      public ExecutionBlock makeExecutionBlock()
347      {
348        SchedulingBlock sb = makeSchedulingBlockButNotScans();
349        sb.setAuthorizedCount(1);
350        sb.submit();
351        
352        //Status will be not-yet-scheduled
353        ExecutionBlock eb = sb.getExecutionBlocks().iterator().next();
354        
355        try
356        {                                   //not-yet-sched                       = 0.2000
357          if (random.nextDouble() < 0.8)
358          {
359            double d = random.nextDouble();
360            if (d < 0.1) //Hold
361            {
362              eb.updateStatusHold();        //on-hold       = 0.8 * 0.1 * 0.5     = 0.0400
363              
364              if (random.nextBoolean())
365                eb.updateStatusRelease();   //not-yet-sched = 0.8 * 0.1 * 0.5     = 0.0400
366            }
367            else if (d < 0.2) //Cancel
368            {
369              eb.updateStatusCancel();      //canceled      = 0.8 * 0.1           = 0.0800
370            }
371            else //Schedule (0.64)
372            {
373              eb.updateStatusSchedule();    //scheduled     = 0.64 * 0.2          = 0.1280
374    
375              d = random.nextDouble();
376              
377              if (d < 0.1) //Hold           
378              {
379                eb.updateStatusHold();      //on-hold       = 0.64 * 0.1 * 0.5    = 0.0320
380                
381                if (random.nextBoolean())   //not-yet-sched = 0.64 * 0.1 * 0.5    = 0.0320
382                  eb.updateStatusRelease();
383              }
384              else if (d < 0.2) //Cancel
385              {
386                eb.updateStatusCancel();    //canceled      = 0.64 * 0.1          = 0.0640
387              }
388              else if (d < 0.8) //Execute
389              {
390                eb.updateStatusExecute();   //in-progress   = 0.64 * 0.6 * 0.2    = 0.0768
391    
392                d = random.nextDouble();
393                
394                if      (d < 0.1) eb.updateStatusFail();     //= 0.64 * 0.6 * 0.1 = 0.0384
395                else if (d < 0.2) eb.updateStatusCancel();   //= 0.64 * 0.6 * 0.1 = 0.0384
396                else if (d < 0.8) eb.updateStatusComplete(); //= 0.64 * 0.6 * 0.6 = 0.2304
397                  
398              }
399            }
400          }
401        }
402        catch (IllegalTransitionException ex)
403        {
404          throw new RuntimeException("PROGRAMMER ERROR in ProjectBuilder.makeExecutionBlock().", ex);
405        }
406        
407        eb.setComments(makeComment());
408        
409        return eb;
410      }
411    
412      //----------------------------------------------------------------------------
413      // Scan
414      //----------------------------------------------------------------------------
415    
416      public void makeScansFor(SchedulingBlock schedBlock)
417      {
418        scanBuilder.makeScansFor(schedBlock.getScanSequence());
419      }
420    
421      public Scan makeScanFor(SchedulingBlock schedBlock)
422      {
423        return scanBuilder.makeScanFor(schedBlock.getScanSequence());
424      }
425      
426      private static final String[] COMMENTS =
427      {
428        "I can't believe they approved my proposal.",
429        "Life was easier before they invented computers.",
430        "Where do I find the headset for listening to my observation?",
431        "How many channels can I get on these big dishes?",
432        "No comment."
433      };
434      
435      private static final String[] COMMENTS_TO_OPER =
436      {
437        "Operator, well let's forget about this call.",
438        "Operator, get me the police!",
439        "Hello, operator, give me number nine.",
440        "Thank you for your time.  Oh, you've been so much more than kind.  You can keep the dime.",
441        "Smooth operator.  Smooth operator."
442      };
443    
444      /** Sometimes sets comment, sometimes removes comments. */
445      public void setComment(Project project)
446      {
447        project.setComments(makeComment());
448      }
449    
450      /** Sometimes sets comment, sometimes removes comments. */
451      public void setComment(ProgramBlock progBlock)
452      {
453        progBlock.setComments(makeComment());
454      }
455    
456      /** Sometimes sets comment, sometimes removes comments. */
457      public void setComment(SchedulingBlock schedBlock)
458      {
459        schedBlock.setComments(makeComment());
460      }
461      
462      private String makeComment()
463      {
464        int size  = COMMENTS.length;
465        int index = random.nextInt(2 * size);
466    
467        return index < size ? COMMENTS[index] : null;
468      }
469    
470      /** Sometimes sets comment, sometimes removes comments. */
471      public void setCommentToOperator(SchedulingBlock sb)
472      {
473        int size  = COMMENTS_TO_OPER.length;
474        int index = random.nextInt(2 * size);
475    
476        sb.setCommentsToOperator(index < size ? COMMENTS_TO_OPER[index] : null);
477      }
478    
479      //============================================================================
480      // HELPERS
481      //============================================================================
482    
483      private String makeCode()
484      {
485        StringBuilder srcName = new StringBuilder();
486    
487        int numChars = random.nextInt(3)+3;
488        for (int i=0; i < numChars; i++)
489          srcName.append((char)('a'+random.nextInt(25)));
490    
491        return srcName.toString();
492      }
493      
494      private TimeOfDay makeTimeOfDay()
495      {
496        TimeOfDay tod = new TimeOfDay();
497        
498        int seconds = random.nextInt(tod.getLengthOfDay()
499                                        .toUnits(TimeUnits.SECOND).intValue());
500        tod.set(new BigDecimal(seconds));
501        
502        return tod;
503      }
504    
505      private TimeOfDayInterval makeTimeOfDayInterval()
506      {
507        return new TimeOfDayInterval(makeTimeOfDay(), makeTimeOfDay());
508      }
509      
510      private TimeInterval makeTimeInterval(Date startTime)
511      {
512        int year, month, day;
513        Date from;
514        Calendar cal = Calendar.getInstance();
515        
516        if (startTime == null)
517        {
518          year  = 1950 + random.nextInt(55);
519          month = random.nextInt(12);
520          day   = 1 + random.nextInt(27);
521          cal.set(year, month, day);
522          from = cal.getTime();
523        }
524        else
525        {
526          from = startTime;
527        }
528        
529        year  = 2006 + random.nextInt(100);
530        month = random.nextInt(12);
531        day   = 1 + random.nextInt(27);
532        cal.set(year, month, day);
533        timeIntervalEnd = cal.getTime();
534        
535        return new TimeInterval(from, timeIntervalEnd);
536      }
537    }