Warning: Can't use blame annotator:
svn blame failed on trunk/src/tmcsim/client/ATMSBatchDriver.java: ("Can't find a temporary directory: Internal error", 20014)

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

Revision 180, 18.9 KB checked in by jdalbey, 9 years ago (diff)

ATMSDriver.java Refactored for cleaner design. DotColor? enum moved to LoopDetector? class. ConsoleDriver? renamed ConsoleTrafficDriver? (and associated project configurations updated), build.xml package-jars target updated, ConsoleTrafficDriver? logic improved to allow more user control, runtraffic.bash script created for automating traffic display tests.

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