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

Revision 210, 15.5 KB checked in by jdalbey, 9 years ago (diff)

Integrate TrafficModelViewer? into the CAD Server GUI as an additional tab. Eliminates the separate window for traffic mgr view.

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