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

Revision 258, 18.0 KB checked in by jdalbey, 7 years ago (diff)

TrafficModelManager?: update to read json output destination from properties file.

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                        notifyObservers(getEventQueue());
202                    }
203                    setChanged();
204                    notifyObservers(currentClock);
205                }
206            }
207        });
208        timer.start();
209
210        if (props.getProperty(PROPERTIES.OUTPUT_DEST.name).equals("FEP"))
211        {
212            // Start the FEP thread (to update ATMS every 30 sec). (See class def below)
213            Thread wtfep = new WriteToFEPThread();
214            wtfep.start();
215        }
216        else
217        {
218            Thread wtConsole = new WriteToConsoleThread();
219            wtConsole.start();
220        }
221        // Always write to json for google map display
222        Thread wtJson = new WriteToJsonThread();
223        wtJson.start();
224    }
225    /** Accessor to event queue
226     *
227     * @return defensive copy of current queue of events
228     */
229    public LinkedList<TrafficEvent> getEventQueue()
230    {
231        return new LinkedList<TrafficEvent>(eventQueue);
232    }
233    public Vector<String> getIncidents()
234    {
235        return new Vector<String>(incidents.keySet());
236    }
237    public String getClockTime()
238    {
239        return currentClock;
240    }
241    public void loadEvents()
242    {
243        // Read the text file of events and put in a queue
244        FileInputStream fis = null;
245        try
246        {
247            fis = new FileInputStream(props.getProperty(PROPERTIES.EVENTS_FILE.name));
248        } catch (FileNotFoundException ex)
249        {
250            Logger.getLogger(TrafficModelManager.class.getName()).log(Level.SEVERE, null, 
251                    "Missing Traffic Events file " + props.getProperty(PROPERTIES.EVENTS_FILE.name));
252            System.exit(-1);
253        }
254        // Read all lines from the file of events
255        Scanner fileScanner = new Scanner(fis);       
256        eventQueue = readBatchFile(fileScanner);
257        // Extract the incidents and create a map
258        incidents = createIncidentMap(eventQueue);
259        setChanged();
260        notifyObservers("-:--");
261        setChanged();
262        notifyObservers(getIncidents());
263        setChanged();
264        notifyObservers(getEventQueue());
265    }
266    /**
267     * This method verifies that the needed configuration properties are not
268     * null.
269     *
270     * @param propertiesFile File path (absolute or relative) to the properties
271     * file containing configuration data.
272     * @return The Properties loaded
273     * @throws SimulationException if there is an exception in verifying the
274     * properties file
275     */
276    public static Properties loadProperties(String propertiesFile)
277            throws SimulationException
278    {
279        Properties props;
280        // Load the properties file.
281        try
282        {
283            props = new Properties();
284            props.load(new FileInputStream(propertiesFile));
285
286        } catch (Exception e)
287        {
288            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
289        }
290
291        // Ensure that the properties file does not have null values for the
292        // required information.
293        if (props.getProperty(PROPERTIES.HIGHWAYS_MAP_FILE.name) == null
294                || props.getProperty(PROPERTIES.FEPSIM_IP_ADDR.name) == null
295                || props.getProperty(PROPERTIES.EVENTS_FILE.name) == null
296                || props.getProperty(PROPERTIES.OUTPUT_DEST.name) == null)
297        {
298            System.out.println("Missing property value in "+propertiesFile);
299            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
300        }
301
302        return props;
303    }
304    /**
305     * Read a file of traffic events. 
306     * @param filename the name of the events file
307     * @return the chronologically ordered list of events
308     */
309    public static LinkedList<TrafficEvent> readBatchFile(Scanner scan)
310    {
311        LinkedList<TrafficEvent> eventList = new LinkedList<TrafficEvent>();
312        while (scan.hasNext())
313        {
314            // Read a line and add it to the event queue
315            String line = scan.nextLine().trim();
316            // Ignore blank lines and comments
317            if (line.length() > 0 && line.charAt(0) != '#')
318            {
319                TrafficEvent evt;
320                try
321                {
322                    evt = new TrafficEvent(line);
323                    eventList.add(evt);
324                }
325                catch (ParseException ex)
326                {
327                    Logger.getLogger(TrafficModelManager.class.getName()).log(Level.SEVERE, null, ex);
328                    System.out.println("Wrong format data in batch event file: " + line + " \nskipping.");
329                    System.out.println("Skipping badly formatted event.");
330                }
331            }
332        }
333        System.out.println("Events file read, " + eventList.size() + " events queued.");
334        // Put the events in chronological order
335        Collections.sort(eventList);
336        return eventList;
337    }
338
339    static Map<String, List<TrafficEvent>> createIncidentMap(LinkedList<TrafficEvent> eventList)
340    {
341        Map<String, List<TrafficEvent>> incidents = new HashMap<String, List<TrafficEvent>>();
342        for (TrafficEvent evt: eventList)
343        {
344            // Add the line to the list for the corresponding incident
345            List evtList;
346            if (incidents.containsKey(evt.incident))
347            {
348                evtList = incidents.get(evt.incident);
349            }
350            else
351            {
352                evtList = new ArrayList<String>();
353            }
354            evtList.add(evt);
355            // and put it back in the map
356            incidents.put(evt.incident, evtList);
357        }       
358        return incidents;
359    }
360   
361    /**
362     * Clear an incident. For each event associated with an incident, turn the
363     * dots in its range Green and remove it from the event queue.
364     *
365     * @param incidentNumber incident to be cleared.
366     */
367    public void clearIncident(String incidentNumber)
368    {
369        boolean ok = incidents.containsKey(incidentNumber);
370        if (!ok)
371        {
372            System.out.println("Sorry, that incident number isn't found.");
373            return;
374        }
375        System.out.println("Clearing incident " + incidentNumber);
376        List<TrafficEvent> events = incidents.get(incidentNumber);
377        // Process each event associated with this incident
378        for (TrafficEvent event : events)
379        {
380            System.out.println("Event: " + event + " cleared.");
381            eventQueue.remove(event);
382
383            // apply colorization to highways, forcing to green, indicating cleared
384            highways.applyColorToHighwayStretch(event.routeNumber, event.dir,
385                    event.postmile, event.range, LoopDetector.DOTCOLOR.GREEN);
386
387        }
388        incidents.remove(incidentNumber);
389        // Now refresh the view with the updated queue of events
390        setChanged();
391        notifyObservers(getEventQueue());
392        setChanged();
393        notifyObservers(getIncidents());
394    }
395
396    /**
397     * Format a time in seconds as HH:MM:SS
398     *
399     * @param seconds
400     * @return HH:MM:SS formatted string
401     */
402    public static String formatTimeInSeconds(final long seconds)
403    {
404        final long hr = TimeUnit.SECONDS.toHours(seconds);
405        final long min = TimeUnit.SECONDS.toMinutes(seconds - TimeUnit.HOURS.toSeconds(hr));
406        final long sec = TimeUnit.SECONDS.toSeconds(seconds - TimeUnit.HOURS.toSeconds(hr) - TimeUnit.MINUTES.toSeconds(min));
407        return String.format("%02d:%02d:%02d", hr, min, sec);
408    }
409
410    class WriteToConsoleThread extends Thread
411    {
412
413        public void run()
414        {
415            System.out.println("WriteToConsole Thread starting.");
416            // Run indefinitely
417            while (true)
418            {
419                 // Write the highway network status to the Console
420                 System.out.println(highways.toString());
421                // Output the highway model
422                String geojson = highways.toJson();
423                //System.out.println(geojson); // diagnostic
424                PrintWriter out;
425                try
426                {
427                    out = new PrintWriter("highways.json");
428                    out.print(geojson);
429                    out.close();
430                }
431                catch (FileNotFoundException ex)
432                {
433                    Logger.getLogger(GoogleMapAnimator.class.getName()).log(Level.SEVERE, null, ex);
434                }
435                // Wait for Google Map to process the data we just sent
436                try
437                {
438                    Thread.sleep(5000);
439                }
440                catch (InterruptedException ie)
441                {
442                    ie.printStackTrace();
443                }
444            }
445
446        }
447    }
448    /** Writes the highway model to a GeoJson file for reading
449     *  by Google Maps.
450     */
451    class WriteToJsonThread extends Thread
452    {
453
454        public void run()
455        {
456            System.out.println("WriteToJson Thread starting.");
457            // Run indefinitely
458            while (true)
459            {
460                 // Write the highway network status to Json
461                String geojson = highways.toJson();
462                PrintWriter out;
463                try
464                {
465                    // currently writes to local file
466                    out = new PrintWriter(jsonPath);
467                    out.print(geojson);
468                    out.close();
469                }
470                catch (FileNotFoundException ex)
471                {
472                    Logger.getLogger(GoogleMapAnimator.class.getName()).log(Level.SEVERE, null, ex);
473                }
474                // Wait for Google Map to process the data we just sent
475                try
476                {
477                    Thread.sleep(30000);
478                }
479                catch (InterruptedException ie)
480                {
481                    ie.printStackTrace();
482                }
483            }
484
485        }
486    }
487   
488    class WriteToFEPThread extends Thread
489    {
490
491        public void run()
492        {
493            System.out.println("WriteToFEP Thread starting.");
494            // Run indefinitely
495            boolean running = true;
496            while (running)
497            {
498                try
499                {
500                    // Write the highway network status to the FEP Simulator
501                    highways.writeToFEP();
502                }
503                catch (SimulationException ex)
504                {
505                    // Ask user if they want to proceed without FEP Sim connection
506//                    int reply = JOptionPane.showConfirmDialog(null, "Failed to connect to FEP Sim, proceed anyway?", "Network Failure", JOptionPane.YES_NO_OPTION);
507//                    if (reply == JOptionPane.NO_OPTION)
508//                    {
509//                        System.exit(0);
510//                    }
511                    System.out.println("Skipping writeToFEP...");
512                    running = false;
513                }
514
515                // Wait for FEP Sim to process the data we just sent
516                try
517                {
518                    Thread.sleep(FEPSIM_INTERVAL);
519                }
520                catch (InterruptedException ie)
521                {
522                    ie.printStackTrace();
523                }
524            }
525
526        }
527    }
528}
Note: See TracBrowser for help on using the repository browser.