source: tmcsimulator/trunk/src/tmcsim/client/ATMSBatchDriver.java @ 183

Revision 183, 17.3 KB checked in by jdalbey, 9 years ago (diff)

ATMSBatchDriver.java Enhanced to automatically sort events chronologically (using new class TrafficEvent?).

Line 
1package tmcsim.client;
2
3import atmsdriver.ConsoleTrafficDriver;
4import atmsdriver.model.Highways;
5import atmsdriver.model.TrafficEvent;
6import atmsdriver.model.LoopDetector.DOTCOLOR;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.io.FileInputStream;
10import java.io.FileNotFoundException;
11import java.rmi.Naming;
12import java.rmi.RemoteException;
13import java.rmi.server.UnicastRemoteObject;
14import java.text.ParseException;
15import java.text.SimpleDateFormat;
16import java.util.ArrayList;
17import java.util.Collections;
18import java.util.Date;
19import java.util.HashMap;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Properties;
24import java.util.Scanner;
25import java.util.concurrent.TimeUnit;
26import java.util.logging.Level;
27import java.util.logging.Logger;
28import javax.swing.JOptionPane;
29import javax.swing.JWindow;
30import javax.swing.Timer;
31import javax.swing.UIManager;
32import tmcsim.common.SimulationException;
33import tmcsim.interfaces.CADClientInterface;
34import tmcsim.interfaces.CoordinatorInterface;
35
36/**
37 * Skeleton for ATMS Driver that reads a "batch" file of highway status update
38 * commands. It operates as a client of the CAD server, using RMI to poll the
39 * server every second for the current simulation clock time. It uses the
40 * simulation clock time to fire update commands at the desired time. Note: Sim
41 * Mgr must be running before starting this application. TODO: We probably want
42 * to be able to "override" a command, to force clearing an incident.
43 *
44 * @author jdalbey
45 */
46public class ATMSBatchDriver extends UnicastRemoteObject implements
47        CADClientInterface
48{
49
50    private static final String CONFIG_FILE_NAME = "cad_client_config.properties";
51    private final static int ONE_SECOND = 1000;
52    private static final int FEPSIM_INTERVAL = 30000;
53    private final static SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
54    /**
55     * Error logger.
56     */
57    private static Logger cadClientLogger = Logger.getLogger("tmcsim.client");
58
59    @Override
60    public void refresh() throws RemoteException
61    {
62        System.out.println("ATMSBatchDriver.refresh() was invoked.");
63    }
64
65    /**
66     * Enumeration containing properties name values. See CADClient class
67     * description for more information.
68     *
69     * @author Matthew Cechini
70     * @see CADClient
71     */
72    private static enum PROPERTIES
73    {
74        CAD_SIM_HOST("CADSimulatorHost"), CAD_SIM_PORT("CADSimulatorSocketPort"), CAD_RMI_PORT(
75                "CADRmiPort"), CLIENT_CAD_POS("CADPosition"), CLIENT_USER_ID(
76                "CADUserID"), KEYBOARD_TYPE("KeyboardType"), DISPLAY_TYPE(
77                "DisplayType");
78        public String name;
79
80        private PROPERTIES(String n)
81        {
82            name = n;
83        }
84    }
85    /**
86     * CADClientSocket Object to handle socket communication between the Client
87     * and CAD Simulator.
88     */
89    private CADClientSocket theClientSocket;
90
91    /**
92     * Properties object for the CADClient class.
93     */
94    private Properties cadClientProp;
95    /**
96     * RMI interface for communication with the remote Coordinator.
97     */
98    private static CoordinatorInterface theCoorInt;
99    /**
100     * reference to itself to be used for disconnecting from CADSimulator
101     */
102    private CADClientInterface client = this;
103
104    /**
105     * Highways in traffic network
106     */
107    final private Highways highways;
108
109    /**
110     * LinkedList of batch events
111     */
112    private LinkedList<TrafficEvent> eventQueue;
113    /**
114     * Map of incidents to events
115     */
116    private Map<String, List<TrafficEvent>> incidents;
117
118    /**
119     * Instance of ConsoleTrafficDriver that contains the highway model
120     */
121    private ConsoleTrafficDriver console;
122
123    /**
124     * GUI for this driver
125     */
126    private ATMSBatchViewer theView;
127
128    /**
129     * Constructor. Initialize data from parsed properties file. Create a socket
130     * connection to the CADSimulator.
131     *
132     * @param propertiesFile File path (absolute or relative) to the properties
133     * file containing configuration data.
134     */
135    public ATMSBatchDriver(String propertiesFile) throws SimulationException,
136            RemoteException
137    {
138        if (!verifyProperties(propertiesFile))
139        {
140            System.exit(0);
141        }
142        // Initialize the highway model
143        incidents = new HashMap<String, List<TrafficEvent>>();
144        highways = new Highways(
145                "config/vds_data/lds.txt",
146                "config/vds_data/loop.txt",
147                "config/vds_data/highwaysMeta.txt",
148                //        "192.168.251.46", 8080);  //IP address of FEP Sim Linux VM
149                "localhost", 8080);
150
151        connect(cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name).trim(),
152                cadClientProp.getProperty(PROPERTIES.CAD_RMI_PORT.name).trim());
153
154        // READ THE BATCH FILE OF COMMANDS and put in a queue
155        readBatchFile();
156        // Launch the display
157        theView = new ATMSBatchViewer(this, new ArrayList<String>(incidents.keySet()));
158        theView.setVisible(true);
159        theView.update("0:00", "1:11", eventQueue);
160
161        // Create a timer that fetches the simulation time every second.
162        Timer timer = new Timer(ONE_SECOND, new ActionListener()
163        {
164            // Every second, see if an event should be launched
165            public void actionPerformed(ActionEvent e)
166            {
167                String currentClock = "";
168                String currentATMStime = "";
169                Date simClock = new Date();
170                // Obtain the simulation time from the CAD server
171                try
172                {
173                    long simtime = theCoorInt.getCurrentSimulationTime();
174                    currentClock = formatInterval(simtime);
175                    // For Debugging, show the ATMS time
176//                    long ATMStime = theCoorInt.getATMStime();       
177//                    Date atmsdate = new Date(ATMStime);
178//                    currentATMStime = formatter.format(atmsdate);
179                    try
180                    {
181                        simClock = formatter.parse(currentClock);
182                    }
183                    catch (ParseException ex)
184                    {
185                        Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
186                        System.out.println("Invalid simulation clock time found in ATMSDriverClient");
187                        System.exit(-1);
188                    }
189                    //System.out.println("Current clock: " + currentClock);
190                }
191                catch (RemoteException ex)
192                {
193                    System.out.println("Remote Exception reading sim or ATMS clock time");
194                    Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
195                }
196                // If we have any events left to process
197                if (!eventQueue.isEmpty())
198                {
199                    // Get the time to launch the next event
200                    TrafficEvent nextEvent = eventQueue.peek();
201                    Date eventTime = nextEvent.eventDate;
202                    //System.out.println("Next event will be launched at: " + formatter.format(eventTime));
203                    // Check the queue of events to see if the first
204                    // item should be launched.  IF so,
205                    // issue that command and remove it from queue.
206                    if (eventTime.before(simClock) || eventTime.equals(simClock))
207                    {
208                        System.out.println("LAUNCHING EVENT: " + nextEvent.toString());
209                        // apply colorization to highways
210                        highways.applyColorToHighwayStretch(nextEvent.routeNumber, nextEvent.dir,
211                                nextEvent.postmile, nextEvent.range, nextEvent.color);
212                        // Remove this event from the queue, we're done with it.
213                        eventQueue.remove();
214                    }
215
216                    theView.update(currentClock, currentATMStime, eventQueue);
217                }
218            }
219        });
220        timer.start();
221
222        // Start the FEP thread (to update ATMS every 30 sec). (See class def below)
223        Thread wtfep = new WriteToFEPThread();
224        wtfep.start();
225
226        ensureProperShutdown();
227    }
228
229    private void readBatchFile()
230    {
231        FileInputStream fis;
232        try
233        {
234            fis = new FileInputStream("config/vds_data/atmsBatchEvents.txt");
235            eventQueue = new LinkedList<TrafficEvent>();
236            // Read all lines from the file of events
237            Scanner scan = new Scanner(fis);
238            while (scan.hasNext())
239            {
240                // Read a line and add it to the event queue
241                String line = scan.nextLine().trim();
242                if (line.charAt(0) != '#')
243                {
244                    TrafficEvent evt;
245                    try
246                    {
247                        evt = new TrafficEvent(line);
248                        eventQueue.add(evt);
249                        String incident = evt.incident;
250                        // Add the line to the list for the corresponding incident
251                        List evtList;
252                        if (incidents.containsKey(evt.incident))
253                        {
254                            evtList = incidents.get(evt.incident);
255                        }
256                        else
257                        {
258                            evtList = new ArrayList<String>();
259                        }
260                        evtList.add(evt);
261                        // and put it back in the map
262                        incidents.put(incident, evtList);
263                    }
264                    catch (ParseException ex)
265                    {
266                        Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
267                        System.out.println("Wrong format data in batch event file: " + line + " \nskipping.");
268                        System.out.println("Skipping badly formatted event.");
269                    }
270                }
271            }
272        }
273        catch (FileNotFoundException ex)
274        {
275            Logger.getLogger(ATMSBatchDriver.class.getName()).log(Level.SEVERE, null, ex);
276        }
277        System.out.println("Events file read, " + eventQueue.size() + " events queued.");
278        // Put the events in chronological order
279        Collections.sort(eventQueue);
280    }
281
282    /**
283     * Clear an incident. For each event associated with an incident, turn the
284     * dots in its range Green and remove it from the event queue.
285     *
286     * @param incidentNumber incident to be cleared.
287     */
288    public void clearIncident(String incidentNumber)
289    {
290        boolean ok = incidents.containsKey(incidentNumber);
291        if (!ok)
292        {
293            System.out.println("Sorry, that incident number isn't found.");
294            return;
295        }
296        System.out.println("Clearing incident " + incidentNumber);
297        List<TrafficEvent> events = incidents.get(incidentNumber);
298        // Process each event associated with this incident
299        for (TrafficEvent event : events)
300        {
301            System.out.println("Event: " + event + " cleared.");
302            eventQueue.remove(event);
303
304            // apply colorization to highways, forcing to green, indicating cleared
305            highways.applyColorToHighwayStretch(event.routeNumber, event.dir,
306                    event.postmile, event.range, DOTCOLOR.GREEN);
307
308        }
309        // Now refresh the view with the updated queue of events
310        theView.update("0:00", "0:00", eventQueue);
311    }
312
313    /**
314     * Connect to the Coordinator's RMI object, and register this object for
315     * callback with the Coordinator.
316     *
317     * @param hostname Host name of the CAD Simulator.
318     * @param portNumber Port number of the CAD Simulator RMI communication.
319     * @throws SimulationException if there is an error creating the RMI
320     * connection.
321     */
322    protected void connect(String hostname, String portNumber)
323            throws SimulationException
324    {
325
326        String coorIntURL = "";
327
328        try
329        {
330            coorIntURL = "rmi://" + hostname + ":" + portNumber
331                    + "/coordinator";
332            theCoorInt = (CoordinatorInterface) Naming.lookup(coorIntURL);
333            theCoorInt.registerForCallback(this);
334        }
335        catch (Exception e)
336        {
337            throw new SimulationException(SimulationException.CAD_SIM_CONNECT,
338                    e);
339
340        }
341    }
342
343    /**
344     * This method verifies that the CAD Simulator Host and Port values are not
345     * null. Also, if a CAD Position or User ID do not exist in the properties
346     * file, the user is prompted to enter values. These values are written to
347     * the properties file. If the user cancels the process of entering these
348     * values, the verification fails.
349     *
350     * @param propertiesFile File path (absolute or relative) to the properties
351     * file containing configuration data.
352     * @return True if the properties file is valid, false if not.
353     * @throws SimulationException if there is an exception in verifying the
354     * properties file, or if the user cancels input.
355     */
356    private boolean verifyProperties(String propertiesFile)
357            throws SimulationException
358    {
359
360        // Load the properties file.
361        try
362        {
363            cadClientProp = new Properties();
364            cadClientProp.load(new FileInputStream(propertiesFile));
365        }
366        catch (Exception e)
367        {
368            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
369                    "Constructor", "Exception in reading properties file.", e);
370
371            throw new SimulationException(SimulationException.INITIALIZE_ERROR,
372                    e);
373        }
374
375        // Ensure that the properties file does not have null values for the
376        // CAD Simulator's connection information.
377        if (cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name) == null
378                || cadClientProp.getProperty(PROPERTIES.CAD_SIM_PORT.name) == null)
379        {
380            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
381                    "Constructor", "Null value in properties file.");
382            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
383        }
384
385        return true;
386    }
387
388    /**
389     * Format a time in seconds as HH:MM:SS
390     *
391     * @param l
392     * @return
393     */
394    private String formatInterval(final long l)
395    {
396        final long hr = TimeUnit.SECONDS.toHours(l);
397        final long min = TimeUnit.SECONDS.toMinutes(l - TimeUnit.HOURS.toSeconds(hr));
398        final long sec = TimeUnit.SECONDS.toSeconds(l - TimeUnit.HOURS.toSeconds(hr) - TimeUnit.MINUTES.toSeconds(min));
399        return String.format("%02d:%02d:%02d", hr, min, sec);
400    }
401
402    public void ensureProperShutdown()
403    {
404        Runtime.getRuntime().addShutdownHook(new Thread()
405        {
406            public void run()
407            {
408                try
409                {
410                    theCoorInt.unregisterForCallback(client);
411                }
412                catch (RemoteException e)
413                {
414                    e.printStackTrace();
415                }
416            }
417        });
418    }
419
420    /**
421     * Construct the CADClient with the properties file path, either from the
422     * command line arguments or default.
423     *
424     * @param args Command line arguments.
425     */
426    public static void main(String[] args)
427    {
428        if (System.getProperty("CONFIG_DIR") == null)
429        {
430            System.setProperty("CONFIG_DIR", "config");
431        }
432
433        try
434        {
435            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
436            new ATMSBatchDriver(System.getProperty("CONFIG_DIR") + System.getProperty("file.separator") + CONFIG_FILE_NAME);
437
438        }
439        catch (Exception e)
440        {
441            cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Main",
442                    "Error initializing application.");
443
444            JOptionPane.showMessageDialog(new JWindow(), e.getMessage(),
445                    "Error - Program Exiting", JOptionPane.ERROR_MESSAGE);
446
447            System.exit(-1);
448        }
449
450    }
451
452    class WriteToFEPThread extends Thread
453    {
454
455        public void run()
456        {
457            System.out.println("WriteToFEP Thread starting.");
458            // Run indefinitely
459            while (true)
460            {
461                try
462                {
463                    // Write the highway network status to the FEP Simulator
464                    highways.writeToFEP();
465                }
466                catch (SimulationException ex)
467                {
468                    // Ask user if they want to proceed without FEP Sim connection
469                    int reply = JOptionPane.showConfirmDialog(null, "Failed to connect to FEP Sim, proceed anyway?", "Network Failure", JOptionPane.YES_NO_OPTION);
470                    if (reply == JOptionPane.NO_OPTION)
471                    {
472                        System.exit(0);
473                    }
474                    System.out.println("Skipping writeToFEP...");
475                }
476
477                // Wait for FEP Sim to process the data we just sent
478                try
479                {
480                    Thread.sleep(FEPSIM_INTERVAL);
481                }
482                catch (InterruptedException ie)
483                {
484                    ie.printStackTrace();
485                }
486            }
487
488        }
489    }
490}
Note: See TracBrowser for help on using the repository browser.