source: tmcsimulator-scriptbuilder/trunk/src/scriptbuilder/structures/SimulationScript.java @ 182

Revision 182, 12.7 KB checked in by jdalbey, 6 years ago (diff)

Modifications to ScriptBuilderFrame?, SimulationScript?, MyScriptHandler? to fix #217

Line 
1package scriptbuilder.structures;
2
3import java.awt.Color;
4import java.io.BufferedWriter;
5import java.io.File;
6import java.io.FileWriter;
7import java.io.IOException;
8import java.util.ArrayList;
9import java.util.List;
10import java.util.Observable;
11import java.util.Vector;
12import javax.xml.parsers.SAXParserFactory;
13import scriptbuilder.structures.ScriptIncident.IncidentFocusedEvent;
14import scriptbuilder.structures.ScriptIncident.SliceChangedEvent;
15import scriptbuilder.structures.events.*;
16import scriptbuilder.structures.units.Unit;
17import java.nio.file.Path;
18import java.nio.file.Paths;
19import java.util.TreeMap;
20
21/**
22 * Representation of the script to be run by the TMC Simulator. Holds a list of
23 * incidents, which have start and end times and contain events.
24 *
25 * @author Greg Eddington <geddingt@calpoly.edu>
26 *
27 * @author Bryan McGuffin <bmcguffi@calpoly.edu>
28 * @author Sebastien Danthinne <sdanthin@calpoly.edu>
29 * @version 2017/06/22
30 */
31public class SimulationScript extends Observable implements I_XML_Writable
32{
33
34    /**
35     * Strings to show in the incident color combo box.  Last item should be black,
36     * which will be used if invalid color is provided to lookupColor().
37     */
38    public static final String[] colorNames = {"BLUE", "RED", "CYAN", "GREEN", 
39        "ORANGE", "MAGENTA", "YELLOW", "BLACK"};
40    /**
41     * Allowed color choices for incident display.
42     * These colors must match the items in colorNames.
43     */
44    public static final Color[] incidentColors = {Color.BLUE, Color.RED, Color.CYAN, 
45        Color.GREEN, Color.ORANGE, Color.MAGENTA, Color.YELLOW, Color.BLACK};
46    /**
47     * The file to which this script will be saved.
48     */
49    public File saveFile = null;
50
51    /**
52     * The name of this script.
53     */
54    public String title = "";
55
56    /**
57     * The incidents displayed by the GUI.
58     */
59    public List<ScriptIncident> incidents;
60
61    /**
62     * The units which participate in Unit events.
63     */
64    public List<Unit> units; 
65
66    //Somewhere in the code, something assumes that the list of incidents
67    //contains exactly 10 items. Until I can find and un-break that, this will do.
68    //todo: this incident fill count error
69    private final int INCIDENT_FILL_COUNT = 10;
70
71    /**
72     * Number of incidents currently displayed.
73     */
74    public int numberOfIncidents;
75
76    /**
77     * Script handler for parsing incoming XML files.
78     */
79    private MyScriptHandler sh;
80   
81    public boolean saved;
82   
83    //TODO: Pretty much everything in this constructor is dummy data.
84    //Replace all of it.
85    /**
86     * Constructor. Backfill incident list with null objects, to be replaced
87     * later.
88     */
89    public SimulationScript()
90    {
91        sh = new MyScriptHandler(this);
92        incidents = new ArrayList<ScriptIncident>();
93        units = new ArrayList<Unit>();
94        numberOfIncidents = 0;
95        saved = true;
96
97        //Backfill with null incidents
98        for (int i = numberOfIncidents; i < INCIDENT_FILL_COUNT; i++)
99        {
100            incidents.add(null);
101        }
102    }
103   
104    /**
105     * checks and sees if this object has the unit passed.
106     * @param unitID
107     * @return does this SimulationScript have unitnum?
108     */
109    public boolean hasUnit(String unitID){
110        boolean indicator = false;
111        if(units.size()!=0){
112            for(Unit u : units){
113               if(unitID.equals(u.UnitNum)){
114                   indicator = true;
115               }
116            }
117        }
118           
119        return indicator;
120    }
121    /**
122     * creates a dummy unit that only has unit number
123     * @param unitNum
124     */
125    public void addDummyUnit(String unitNum){
126        Unit dummy = new Unit();
127        dummy.UnitNum = unitNum;
128        units.add(dummy);
129    }
130    /**
131     * Update the script's observers.
132     *
133     */
134    public void update()
135    {
136        // The script has changed, notify observers
137        //use to rewrite the save indicator
138        //System.out.println("Script changed");
139        setChanged();
140        notifyObservers(this);
141        saved = false;
142       
143       
144    }
145   
146
147    /**
148     * Tell this script's observers that there is a new slice event.
149     *
150     * @param e the slice focus event
151     */
152    public void broadcastEvent(SliceChangedEvent e)
153    {
154        // The slice focus has changed; pass the message
155        setChanged();
156        notifyObservers(e);
157    }
158
159    /**
160     * Tell this script's observers that there is a new slice event.
161     *
162     * @param e the incident focus event
163     */
164    public void broadcastEvent(IncidentFocusedEvent e)
165    {
166        // The slice focus has changed; pass the message
167        setChanged();
168        notifyObservers(e);
169    }
170
171    /**
172     * Load in an existing script from an XML file.
173     *
174     * @param f the file containing the script
175     */
176    public void loadScriptFromFile(File f)
177    {
178        try
179        {
180
181            SAXParserFactory.newInstance().newSAXParser().parse(f, sh);
182
183            Vector<ScriptIncident> inc = sh.getIncidents();
184            units = sh.getUnits();
185            for (ScriptIncident sci : inc)
186            {
187                addIncident(sci);
188            }
189        }
190        catch (Exception ex)
191        {
192            System.out.println("ERROR LOADING SCRIPT");
193            ex.printStackTrace();
194        }
195        System.out.println("H");
196        for(Unit testUnit : units){
197            System.out.println(testUnit.toXML());
198        }
199        this.update();
200    }
201   
202    /**
203     * Load in an existing list of units from an XML file.
204     * @param inStream the input stream for the file containing the units
205     */
206    public void loadUnitsFromFile(java.io.InputStream inStream){
207        try
208        {
209            SAXParserFactory.newInstance().newSAXParser().parse(inStream, sh);
210            units.addAll(sh.getUnits());
211        }
212        catch (Exception ex)
213        {
214            System.out.println("ERROR LOADING UNITS");
215            ex.printStackTrace();
216        }
217        this.update();
218    }
219
220    /**
221     * Add a new incident to the script.
222     *
223     * @param sci the incident to be added.
224     * @return true if there was enough room to add this incident.
225     */
226    public boolean addIncident(ScriptIncident sci)
227    {
228        if (numberOfIncidents < INCIDENT_FILL_COUNT)
229        {
230            incidents.set(numberOfIncidents++, sci);
231            return true;
232        }
233        return false;
234    }
235
236    /**
237     * Write this script, in proper XML format, to the file in question.
238     *
239     * @param f the destination savefile to be written.
240     */
241    public void saveScriptToFile(File f)
242    {
243        try
244        {
245            f.createNewFile();
246
247            BufferedWriter bw = new BufferedWriter(new FileWriter(f));
248            bw.write(this.toXML());
249            bw.flush();
250            bw.close();
251
252        }
253        catch (Exception ex)
254        {
255            System.out.println("ERROR SAVING SCRIPT");
256            ex.printStackTrace();
257        }
258        try
259        {
260            createAudioDirectory(Paths.get(f.getCanonicalPath()).getParent());
261        }
262        catch(IOException ex)
263        {
264            System.err.println("there was a problem creating the audio directories.");
265        }
266       
267       
268        saved = true;
269    }
270   
271    /**
272     * Creates the proper audio directory using the I_AudioEvent types.
273     * @param path of audio directory root
274     */
275    private void createAudioDirectory(Path path){
276        File f = new File(path.toString()+"/audio");
277        if(!f.exists())
278        {
279            f.mkdir();
280            //scan through to see what is already there and if there is no existing audio directory, create it.
281        }
282        for(ScriptIncident i : incidents)
283        {
284            if(i!=null)
285            {
286                String name = ((Integer) i.number).toString();
287                File incidentFolder = new File(path.toString()+"/audio/"+name);
288                if(!incidentFolder.exists())
289                {
290                    //create an incidentfolder since one does not already exist
291                    incidentFolder.mkdir();
292                }
293
294               
295                for(TimeSlice slice : i.getSlices())
296                {
297                    for(I_ScriptEvent event : slice.events)
298                    {
299                        if(event instanceof I_AudioEvent)
300                        {
301                            //if the event is a chp radio event
302                            //then add the dummy file to the subdirectory created
303                            //CURRENTLY this ONLY is implemented for CHPRadioEvent, so it will need to be changed later.
304                            CHPRadioEvent radioEvent = (CHPRadioEvent) event;
305
306                            String output = radioEvent.toScriptFile();
307                            //optimally, this line should use the ID, but to account for files being read in, it needs to be the radiofile name
308                            File newAudioScript = new File(path.toString()+"/audio/"+name+"/"+radioEvent.radioFile.replaceAll(".mp3","")+".txt");
309                           
310                            try
311                            {
312                                newAudioScript.createNewFile();
313                                BufferedWriter bw = new BufferedWriter(new FileWriter(newAudioScript));
314                                bw.write(output);
315                                bw.flush();
316                                bw.close();
317                            }catch(Exception e)
318                            {
319                                System.err.println("there was a problem creating your text files");
320                            }
321
322                        }
323                    }
324                }
325
326               
327            }
328
329           
330        }
331       
332       
333    }
334    @Override
335    public String toXML()
336    {
337        ArrayList<TimeSlice> slices = arrangeAllSlices();
338        String output = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
339        output += XMLWriter.internalDTD();
340        output += XMLWriter.openTag(ELEMENT.TMC_SCRIPT.tag + " title=\"" + this.title + "\"");
341
342        if (units.size() > 0)
343        {
344            output += XMLWriter.openTag(ELEMENT.SCRIPT_DATA.tag);
345            for (Unit unit : units)
346            {
347                output += unit.toXML();
348            }
349            output += XMLWriter.closeTag(ELEMENT.SCRIPT_DATA.tag);
350        }
351        for (TimeSlice slice : slices)
352        {
353            output += slice.toXML();
354        }
355        output += XMLWriter.closeTag(ELEMENT.TMC_SCRIPT.tag);
356        return output;
357    }
358
359    /**
360     * Arranges all timeslices in this script in chronological order, then by
361     * incident number.
362     *
363     * @return a list of all timeslices in the simulation script
364     */
365    public ArrayList<TimeSlice> arrangeAllSlices()
366    {
367        ArrayList<TimeSlice> list = new ArrayList<TimeSlice>();
368        int length = absoluteLength();
369
370        for (int i = 0; i < length; i++)
371        {
372            for (ScriptIncident inc : incidents)
373            {
374
375                if (inc != null && inc.slices.get(i) != null)
376                {
377                    list.add(inc.slices.get(i));
378                }
379            }
380        }
381        return list;
382    }
383
384    /**
385     * Gets the total length of the simulation in seconds.
386     *
387     * @return
388     */
389    public int absoluteLength()
390    {
391        int length = 0;
392        for (ScriptIncident inc : incidents)
393        {
394            if (inc != null)
395            {
396                inc.updateLength();
397                int currentLength = inc.length + inc.offset;
398                if (currentLength > length)
399                {
400                    length = currentLength;
401                }
402            }
403        }
404        return length;
405    }
406
407    /**
408     * Counts the number of incidents currently running.
409     *
410     * @return the number of non-null incidents currently in the script. A
411     * number between 0 and INCIDENT_FILL_COUNT, inclusive.
412     */
413    public int incidentCount()
414    {
415        int count = 0;
416        for (ScriptIncident inc : incidents)
417        {
418            if (inc != null)
419            {
420                count++;
421            }
422        }
423        return count;
424    }
425   
426    /** Given a color, find its index in the incidentColors.
427     * @param color a java color
428     * @return the index of color in incidentColors, or last index if color isn't
429     * in incidentColors.  The last item in incidentColors should be black.
430     */
431    public static int lookupColor(Color color)
432    {
433        int idx = 0;
434        // search color array for target
435        while(idx < incidentColors.length && !incidentColors[idx].equals(color))
436        { 
437            idx++;
438        }
439        // if color not found, return index of last item.
440        if (idx == incidentColors.length) 
441        {
442            return idx-1;
443        }
444        else return idx;
445    }
446}
Note: See TracBrowser for help on using the repository browser.