source: tmcsimulator/trunk/src/tmcsim/cadsimulator/managers/TrafficModelManager.java @ 277

Revision 277, 18.2 KB checked in by jdalbey, 7 years ago (diff)

Update TrafficModelView? so expired events remain on display but the list scrolls the selected item.

Line 
1package tmcsim.cadsimulator.managers;
2
3import atmsdriver.GoogleMapAnimator;
4import atmsdriver.model.Highways;
5import atmsdriver.model.LoopDetector;
6import atmsdriver.model.TrafficEvent;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.io.FileInputStream;
10import java.io.FileNotFoundException;
11import java.io.PrintWriter;
12import java.rmi.RemoteException;
13import java.text.ParseException;
14import java.text.SimpleDateFormat;
15import java.util.ArrayList;
16import java.util.Collections;
17import java.util.Date;
18import java.util.HashMap;
19import java.util.LinkedList;
20import java.util.List;
21import java.util.Map;
22import java.util.Observable;
23import java.util.Properties;
24import java.util.Scanner;
25import java.util.Vector;
26import java.util.concurrent.TimeUnit;
27import java.util.logging.Level;
28import java.util.logging.Logger;
29import javax.swing.JOptionPane;
30import javax.swing.JWindow;
31import javax.swing.Timer;
32import javax.swing.UIManager;
33import tmcsim.cadsimulator.Coordinator;
34import tmcsim.cadsimulator.viewer.model.CADSimulatorState;
35import tmcsim.common.SimulationException;
36
37/**
38 * Traffic Model Manager is a model and controller for the Traffic Model
39 * used in the simulation.  It represents all the highways and traffic
40 * events that occur.
41 * @author jdalbey
42 * @version 2.0
43 */
44public class TrafficModelManager extends Observable
45{
46    private final static int ONE_SECOND = 1000;
47    private static final int FEPSIM_INTERVAL = 30000;
48    private final static SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
49
50    /**
51     * Error Logger.
52     */
53    private static Logger logger = Logger.getLogger("tmcsim.cadsimulator.managers");
54
55    /**
56     * Enumeration containing property names for Properties parsing.
57     *
58     * @author Matthew Cechini
59     */
60    private static enum PROPERTIES
61    {
62        HIGHWAYS_MAP_FILE("Highways_Map_File"),
63        FEPSIM_IP_ADDR("FEPSim_IP_addr"),
64        EVENTS_FILE("Events_File"),
65        OUTPUT_DEST("Output_Destination"),
66        JSON_PATH("Json_Path");
67       
68        public String name;
69
70        private PROPERTIES(String n)
71        {
72            name = n;
73        }
74    };
75
76
77    /**
78     * Properties Object.
79     */
80    private Properties props = null;
81   
82    /**
83     * The Coordinator object from which we obtain the simulation clock.
84     */ 
85    private Coordinator theCoordinator;
86
87    /**
88     * Highways in traffic network
89     */
90    private Highways highways;
91
92    /**
93     * LinkedList of batch events
94     */
95    private LinkedList<TrafficEvent> eventQueue;
96    /**
97     * Map of incidents to events
98     */
99    private Map<String, List<TrafficEvent>> incidents;
100
101    /**
102     * Current simulation clock time
103     */
104    private String currentClock = "";
105   
106    /**
107     * Path for writing highway data to Json file.
108     */
109    private String jsonPath;
110   
111    /**
112     * Constructor. Loads the Properties file and initializes the
113     * highway network model.
114     *
115     * @param propertiesFile Target file path of properties file.
116     * @param theCoordinator Reference to the simulation Coordinator from whom we get simulation
117     * elapsed time.
118     */
119    public TrafficModelManager(String propertiesFile, final Coordinator theCoordinator) 
120            throws SimulationException
121    {
122        try 
123        {
124            props = loadProperties(propertiesFile);
125            jsonPath = props.getProperty(PROPERTIES.JSON_PATH.name);
126            logger.logp(Level.INFO, "Traffic Managr", "Constructor", 
127                    "Highway network json output: " + jsonPath);
128            // Initialize the highway model
129            incidents = new HashMap<String, List<TrafficEvent>>();
130            highways = new Highways(
131                    props.getProperty(PROPERTIES.HIGHWAYS_MAP_FILE.name),
132                    props.getProperty(PROPERTIES.FEPSIM_IP_ADDR.name),
133                    8080); 
134            this.theCoordinator = theCoordinator;
135        }
136        catch (Exception e) 
137        {
138            logger.logp(Level.SEVERE, "Traffic Manager", "Constructor",
139                    "Exception in parsing properties file.", e);
140        }
141    }
142    /**
143     * Load the traffic events and start processing the event queue.
144     * Usage: addObserver must be called before calling run.
145     */
146    public void run()
147    {
148        loadEvents();
149
150        // Create a timer that fetches the simulation time every second.
151        Timer timer = new Timer(ONE_SECOND, new ActionListener()
152        {
153            // Every second, see if an event should be launched
154            public void actionPerformed(ActionEvent e)
155            {
156                String currentATMStime = "";
157                Date simClock = new Date();
158                // Obtain the simulation time from the CAD server
159                try
160                {
161                    long simtime = theCoordinator.getCurrentSimulationTime();
162                    currentClock = formatTimeInSeconds(simtime);
163                    // For Debugging, show the ATMS time
164//                    long ATMStime = theCoorInt.getATMStime();       
165//                    Date atmsdate = new Date(ATMStime);
166//                    currentATMStime = formatter.format(atmsdate);
167                    try
168                    {
169                        simClock = formatter.parse(currentClock);
170                    }
171                    catch (ParseException ex)
172                    {
173                        Logger.getLogger(TrafficModelManager.class.getName()).log(Level.SEVERE, null, ex);
174                        System.out.println("Invalid simulation clock time found");
175                        System.exit(-1);
176                    }
177                }
178                catch (RemoteException ex)
179                {
180                    System.out.println("Remote Exception reading sim or ATMS clock time");
181                    Logger.getLogger(TrafficModelManager.class.getName()).log(Level.SEVERE, null, ex);
182                }
183                // If we have any events left to process
184                if (!eventQueue.isEmpty())
185                {
186                    // Get the time to launch the next event
187                    TrafficEvent nextEvent = eventQueue.peek();
188                    Date eventTime = nextEvent.eventDate;
189                    // Check the queue of events to see if the first
190                    // item should be launched.  IF so,
191                    // issue that command and remove it from queue.
192                    if (eventTime.before(simClock) || eventTime.equals(simClock))
193                    {
194                        System.out.println("LAUNCHING EVENT: " + nextEvent.toString());
195                        // apply colorization to highways
196                        highways.applyColorToHighwayStretch(nextEvent.routeNumber, nextEvent.dir,
197                                nextEvent.postmile, nextEvent.range, nextEvent.color);
198                        // Remove this event from the queue, we're done with it.
199                        eventQueue.remove();
200                        setChanged();
201                        // Send updated list to view
202                        // notifyObservers(getEventQueue());
203                        // Notify view it should scroll to next event
204                        notifyObservers(new Integer(0));
205                    }
206                    setChanged();
207                    notifyObservers(currentClock);
208                }
209            }
210        });
211        timer.start();
212
213        if (props.getProperty(PROPERTIES.OUTPUT_DEST.name).equals("FEP"))
214        {
215            // Start the FEP thread (to update ATMS every 30 sec). (See class def below)
216            Thread wtfep = new WriteToFEPThread();
217            wtfep.start();
218        }
219        else
220        {
221            Thread wtConsole = new WriteToConsoleThread();
222            wtConsole.start();
223        }
224        // Always write to json for google map display
225        Thread wtJson = new WriteToJsonThread();
226        wtJson.start();
227    }
228    /** Accessor to event queue
229     *
230     * @return defensive copy of current queue of events
231     */
232    public LinkedList<TrafficEvent> getEventQueue()
233    {
234        return new LinkedList<TrafficEvent>(eventQueue);
235    }
236    public Vector<String> getIncidents()
237    {
238        return new Vector<String>(incidents.keySet());
239    }
240    public String getClockTime()
241    {
242        return currentClock;
243    }
244    public void loadEvents()
245    {
246        // Read the text file of events and put in a queue
247        FileInputStream fis = null;
248        try
249        {
250            fis = new FileInputStream(props.getProperty(PROPERTIES.EVENTS_FILE.name));
251        } catch (FileNotFoundException ex)
252        {
253            Logger.getLogger(TrafficModelManager.class.getName()).log(Level.SEVERE, null, 
254                    "Missing Traffic Events file " + props.getProperty(PROPERTIES.EVENTS_FILE.name));
255            System.exit(-1);
256        }
257        // Read all lines from the file of events
258        Scanner fileScanner = new Scanner(fis);       
259        eventQueue = readBatchFile(fileScanner);
260        // Extract the incidents and create a map
261        incidents = createIncidentMap(eventQueue);
262        setChanged();
263        notifyObservers("-:--");
264        setChanged();
265        notifyObservers(getIncidents());
266        setChanged();
267        notifyObservers(getEventQueue());
268    }
269    /**
270     * This method verifies that the needed configuration properties are not
271     * null.
272     *
273     * @param propertiesFile File path (absolute or relative) to the properties
274     * file containing configuration data.
275     * @return The Properties loaded
276     * @throws SimulationException if there is an exception in verifying the
277     * properties file
278     */
279    public static Properties loadProperties(String propertiesFile)
280            throws SimulationException
281    {
282        Properties props;
283        // Load the properties file.
284        try
285        {
286            props = new Properties();
287            props.load(new FileInputStream(propertiesFile));
288
289        } catch (Exception e)
290        {
291            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
292        }
293
294        // Ensure that the properties file does not have null values for the
295        // required information.
296        if (props.getProperty(PROPERTIES.HIGHWAYS_MAP_FILE.name) == null
297                || props.getProperty(PROPERTIES.FEPSIM_IP_ADDR.name) == null
298                || props.getProperty(PROPERTIES.EVENTS_FILE.name) == null
299                || props.getProperty(PROPERTIES.OUTPUT_DEST.name) == null)
300        {
301            System.out.println("Missing property value in "+propertiesFile);
302            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
303        }
304
305        return props;
306    }
307    /**
308     * Read a file of traffic events. 
309     * @param filename the name of the events file
310     * @return the chronologically ordered list of events
311     */
312    public static LinkedList<TrafficEvent> readBatchFile(Scanner scan)
313    {
314        LinkedList<TrafficEvent> eventList = new LinkedList<TrafficEvent>();
315        while (scan.hasNext())
316        {
317            // Read a line and add it to the event queue
318            String line = scan.nextLine().trim();
319            // Ignore blank lines and comments
320            if (line.length() > 0 && line.charAt(0) != '#')
321            {
322                TrafficEvent evt;
323                try
324                {
325                    evt = new TrafficEvent(line);
326                    eventList.add(evt);
327                }
328                catch (ParseException ex)
329                {
330                    Logger.getLogger(TrafficModelManager.class.getName()).log(Level.SEVERE, null, ex);
331                    System.out.println("Wrong format data in batch event file: " + line + " \nskipping.");
332                    System.out.println("Skipping badly formatted event.");
333                }
334            }
335        }
336        System.out.println("Events file read, " + eventList.size() + " events queued.");
337        // Put the events in chronological order
338        Collections.sort(eventList);
339        return eventList;
340    }
341
342    static Map<String, List<TrafficEvent>> createIncidentMap(LinkedList<TrafficEvent> eventList)
343    {
344        Map<String, List<TrafficEvent>> incidents = new HashMap<String, List<TrafficEvent>>();
345        for (TrafficEvent evt: eventList)
346        {
347            // Add the line to the list for the corresponding incident
348            List evtList;
349            if (incidents.containsKey(evt.incident))
350            {
351                evtList = incidents.get(evt.incident);
352            }
353            else
354            {
355                evtList = new ArrayList<String>();
356            }
357            evtList.add(evt);
358            // and put it back in the map
359            incidents.put(evt.incident, evtList);
360        }       
361        return incidents;
362    }
363   
364    /**
365     * Clear an incident. For each event associated with an incident, turn the
366     * dots in its range Green and remove it from the event queue.
367     *
368     * @param incidentNumber incident to be cleared.
369     */
370    public void clearIncident(String incidentNumber)
371    {
372        boolean ok = incidents.containsKey(incidentNumber);
373        if (!ok)
374        {
375            System.out.println("Sorry, that incident number isn't found.");
376            return;
377        }
378        System.out.println("Clearing incident " + incidentNumber);
379        List<TrafficEvent> events = incidents.get(incidentNumber);
380        // Process each event associated with this incident
381        for (TrafficEvent event : events)
382        {
383            System.out.println("Event: " + event + " cleared.");
384            eventQueue.remove(event);
385
386            // apply colorization to highways, forcing to green, indicating cleared
387            highways.applyColorToHighwayStretch(event.routeNumber, event.dir,
388                    event.postmile, event.range, LoopDetector.DOTCOLOR.GREEN);
389
390        }
391        incidents.remove(incidentNumber);
392        // Now refresh the view with the updated queue of events
393        setChanged();
394        notifyObservers(getEventQueue());
395        setChanged();
396        notifyObservers(getIncidents());
397    }
398
399    /**
400     * Format a time in seconds as HH:MM:SS
401     *
402     * @param seconds
403     * @return HH:MM:SS formatted string
404     */
405    public static String formatTimeInSeconds(final long seconds)
406    {
407        final long hr = TimeUnit.SECONDS.toHours(seconds);
408        final long min = TimeUnit.SECONDS.toMinutes(seconds - TimeUnit.HOURS.toSeconds(hr));
409        final long sec = TimeUnit.SECONDS.toSeconds(seconds - TimeUnit.HOURS.toSeconds(hr) - TimeUnit.MINUTES.toSeconds(min));
410        return String.format("%02d:%02d:%02d", hr, min, sec);
411    }
412
413    class WriteToConsoleThread extends Thread
414    {
415
416        public void run()
417        {
418            System.out.println("WriteToConsole Thread starting.");
419            // Run indefinitely
420            while (true)
421            {
422                 // Write the highway network status to the Console
423                 System.out.println(highways.toString());
424                // Output the highway model
425                String geojson = highways.toJson();
426                //System.out.println(geojson); // diagnostic
427                PrintWriter out;
428                try
429                {
430                    out = new PrintWriter("highways.json");
431                    out.print(geojson);
432                    out.close();
433                }
434                catch (FileNotFoundException ex)
435                {
436                    Logger.getLogger(GoogleMapAnimator.class.getName()).log(Level.SEVERE, null, ex);
437                }
438                // Wait for Google Map to process the data we just sent
439                try
440                {
441                    Thread.sleep(5000);
442                }
443                catch (InterruptedException ie)
444                {
445                    ie.printStackTrace();
446                }
447            }
448
449        }
450    }
451    /** Writes the highway model to a GeoJson file for reading
452     *  by Google Maps.
453     */
454    class WriteToJsonThread extends Thread
455    {
456
457        public void run()
458        {
459            System.out.println("WriteToJson Thread starting.");
460            // Run indefinitely
461            while (true)
462            {
463                 // Write the highway network status to Json
464                String geojson = highways.toJson();
465                PrintWriter out;
466                try
467                {
468                    // currently writes to local file
469                    out = new PrintWriter(jsonPath);
470                    out.print(geojson);
471                    out.close();
472                }
473                catch (FileNotFoundException ex)
474                {
475                    Logger.getLogger(GoogleMapAnimator.class.getName()).log(Level.SEVERE, null, ex);
476                }
477                // Wait for Google Map to process the data we just sent
478                try
479                {
480                    Thread.sleep(30000);
481                }
482                catch (InterruptedException ie)
483                {
484                    ie.printStackTrace();
485                }
486            }
487
488        }
489    }
490   
491    class WriteToFEPThread extends Thread
492    {
493
494        public void run()
495        {
496            System.out.println("WriteToFEP Thread starting.");
497            // Run indefinitely
498            boolean running = true;
499            while (running)
500            {
501                try
502                {
503                    // Write the highway network status to the FEP Simulator
504                    highways.writeToFEP();
505                }
506                catch (SimulationException ex)
507                {
508                    // Ask user if they want to proceed without FEP Sim connection
509//                    int reply = JOptionPane.showConfirmDialog(null, "Failed to connect to FEP Sim, proceed anyway?", "Network Failure", JOptionPane.YES_NO_OPTION);
510//                    if (reply == JOptionPane.NO_OPTION)
511//                    {
512//                        System.exit(0);
513//                    }
514                    System.out.println("Skipping writeToFEP...");
515                    running = false;
516                }
517
518                // Wait for FEP Sim to process the data we just sent
519                try
520                {
521                    Thread.sleep(FEPSIM_INTERVAL);
522                }
523                catch (InterruptedException ie)
524                {
525                    ie.printStackTrace();
526                }
527            }
528
529        }
530    }
531}
Note: See TracBrowser for help on using the repository browser.