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

Revision 135, 17.8 KB checked in by jdalbey, 9 years ago (diff)

ATMSBatchDriver.java Implemented "clear incident" feature.

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